1 /*
2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 #pragma ident "%Z%%M% %I% %E% SMI"
7
8 /****************************************************************************
9
10 Copyright (c) 1999,2000 WU-FTPD Development Group.
11 All rights reserved.
12
13 Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
14 The Regents of the University of California.
15 Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
16 Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
17 Portions Copyright (c) 1989 Massachusetts Institute of Technology.
18 Portions Copyright (c) 1998 Sendmail, Inc.
19 Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
20 Portions Copyright (c) 1997 by Stan Barber.
21 Portions Copyright (c) 1997 by Kent Landfield.
22 Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
23 Free Software Foundation, Inc.
24
25 Use and distribution of this software and its source code are governed
26 by the terms and conditions of the WU-FTPD Software License ("LICENSE").
27
28 If you did not receive a copy of the license, it may be obtained online
29 at http://www.wu-ftpd.org/license.html.
30
31 $Id: ftpshut.c,v 1.12 2000/07/01 18:17:39 wuftpd Exp $
32
33 ****************************************************************************/
34 /* ftpshut
35 * =======
36 * creates the ftpd shutdown file.
37 */
38
39 #include "config.h"
40
41 #include <errno.h>
42 #include <pwd.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <ctype.h>
46 #ifdef TIME_WITH_SYS_TIME
47 #include <time.h>
48 #include <sys/time.h>
49 #else
50 #ifdef HAVE_SYS_TIME_H
51 #include <sys/time.h>
52 #else
53 #include <time.h>
54 #endif
55 #endif
56
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <sys/file.h>
60 #include <sys/param.h>
61 #if defined(VIRTUAL) && defined(INET6)
62 #include <netinet/in.h>
63 #endif
64
65 #include "pathnames.h"
66
67 #define WIDTH 70
68
69 int verbose = 0;
70 int denyoffset = 10; /* default deny time */
71 int discoffset = 5; /* default disc time */
72 char *message = "System shutdown at %s"; /* default message */
73
74 struct tm *tp;
75
76 #define MAXVIRTUALS 512
77
78 char *progname;
79 char *msgfiles[MAXVIRTUALS];
80 int numfiles = 0;
81
82 #ifdef VIRTUAL
83 extern int read_servers_line(FILE *, char *, size_t, char *, size_t);
84 #endif
85 void print_copyright(void);
86
87 static int newfile(char *fpath)
88 {
89 int i;
90 int fnd;
91
92 /*
93 ** Check to see if the message file path has already been
94 ** seen. If so then there is no need to create it again.
95 */
96
97 fnd = 0;
98 for (i = 0; i < numfiles; i++) {
99 if (strcmp(msgfiles[i], fpath) == 0) {
100 fnd = 1;
101 break;
102 }
103 }
104 if (!fnd) {
105 msgfiles[numfiles++] = strdup(fpath);
106 return (1);
107 }
108 return (0);
109 }
110
111 static int shutdown_msgfile(char *filename, char *buffer)
112 {
113 FILE *fp;
114 mode_t oldmask;
115
116 oldmask = umask(022);
117 fp = fopen(filename, "w");
118 (void) umask(oldmask);
119 if (fp == NULL) {
120 fprintf(stderr, "%s: could not open shutdown file %s: %s\n",
121 progname, filename, strerror(errno));
122 return (1);
123 }
124
125 fprintf(fp, "%.4d %.2d %.2d %.2d %.2d %.4d %.4d\n",
126 (tp->tm_year) + 1900,
127 tp->tm_mon,
128 tp->tm_mday,
129 tp->tm_hour,
130 tp->tm_min,
131 denyoffset,
132 discoffset);
133 fprintf(fp, "%s\n", buffer);
134 fclose(fp);
135 if (verbose)
136 printf("%s: %s created\n", progname, filename);
137 return (0);
138 }
139
140 static void massage(char *buf)
141 {
142 char *sp = NULL;
143 char *ptr;
144 int i = 0;
145 int j = 0;
146
147 ptr = buf;
148
149 while (*ptr++ != '\0') {
150 ++i;
151
152 /* if we have a space, keep track of where and at what "count" */
153
154 if (*ptr == ' ') {
155 sp = ptr;
156 j = i;
157 }
158 /* magic cookies... */
159
160 if (*ptr == '%') {
161 ++ptr;
162 switch (*ptr) {
163 case 'r':
164 case 's':
165 case 'd':
166 case 'T':
167 i = i + 24;
168 break;
169 case '\n':
170 i = 0;
171 break;
172 case 'C':
173 case 'R':
174 case 'L':
175 case 'U':
176 i = i + 10;
177 break;
178 case 'M':
179 case 'N':
180 i = i + 3;
181 break;
182 case '\0':
183 return;
184 /* break; */
185 default:
186 i = i + 1;
187 break;
188 }
189 }
190 /* break up the long lines... */
191
192 if ((i >= WIDTH) && (sp != NULL)) {
193 *sp = '\n';
194 sp = NULL;
195 i = i - j;
196 }
197 }
198 }
199
200 static void usage(int exitval)
201 {
202 fprintf(stderr,
203 "Usage: %s [-d min] [-l min] now [\"message\"]\n", progname);
204 fprintf(stderr,
205 " %s [-d min] [-l min] +dd [\"message\"]\n", progname);
206 fprintf(stderr,
207 " %s [-d min] [-l min] HHMM [\"message\"]\n", progname);
208 exit(exitval);
209 }
210
211 int main(int argc, char **argv)
212 {
213 time_t c_time = 0;
214
215 char buf[BUFSIZ];
216
217 int c;
218 extern int optind;
219 extern char *optarg;
220
221 FILE *accessfile;
222 char *aclbuf, *myaclbuf, *crptr;
223 char *sp = NULL;
224 char linebuf[1024];
225 char shutmsg[BUFSIZ];
226 char anonpath[MAXPATHLEN];
227 struct stat finfo;
228 struct passwd *pwent;
229
230 #ifdef VIRTUAL
231 char *cp = NULL;
232 FILE *svrfp;
233 #ifdef INET6
234 char hostaddress[INET6_ADDRSTRLEN];
235 #else
236 char hostaddress[32];
237 #endif
238 char root[MAXPATHLEN];
239 char accesspath[MAXPATHLEN];
240 char configdir[MAXPATHLEN];
241 char altmsgpath[MAXPATHLEN];
242 #endif
243
244 if ((progname = strrchr(argv[0], '/')))
245 ++progname;
246 else
247 progname = argv[0];
248
249 while ((c = getopt(argc, argv, "vVl:d:")) != EOF) {
250 switch (c) {
251 case 'v':
252 verbose++;
253 break;
254 case 'l':
255 denyoffset = atoi(optarg);
256 break;
257 case 'd':
258 discoffset = atoi(optarg);
259 break;
260 case 'V':
261 print_copyright();
262 exit(0);
263 default:
264 usage(-1);
265 }
266 }
267
268 if ((accessfile = fopen(_PATH_FTPACCESS, "r")) == NULL) {
269 if (errno != ENOENT)
270 fprintf(stderr, "%s: could not open access file %s: %s\n",
271 progname, _PATH_FTPACCESS, strerror(errno));
272 exit(1);
273 }
274 if (fstat(fileno(accessfile), &finfo) != 0) {
275 fprintf(stderr, "%s: could not fstat() access file %s: %s\n",
276 progname, _PATH_FTPACCESS, strerror(errno));
277 exit(1);
278 }
279 if (finfo.st_size == 0) {
280 fprintf(stderr, "%s: no shutdown path defined in ftpaccess file %s.\n",
281 progname, _PATH_FTPACCESS);
282 exit(1);
283 }
284 else {
285 if (!(aclbuf = (char *) malloc(finfo.st_size + 1))) {
286 fprintf(stderr, "%s: could not malloc aclbuf: %s\n",
287 progname, strerror(errno));
288 exit(1);
289 }
290 fread(aclbuf, finfo.st_size, 1, accessfile);
291 *(aclbuf + finfo.st_size) = '\0';
292 }
293
294 myaclbuf = aclbuf;
295 while (*myaclbuf != '\0') {
296 if (strncasecmp(myaclbuf, "shutdown", 8) == 0) {
297 for (crptr = myaclbuf; *crptr++ != '\n';);
298 *--crptr = '\0';
299 (void) strlcpy(linebuf, myaclbuf, sizeof(linebuf));
300 *crptr = '\n';
301 (void) strtok(linebuf, " \t"); /* returns "shutdown" */
302 sp = strtok(NULL, " \t"); /* returns shutdown path */
303 /* save for future use */
304 (void) strlcpy(shutmsg, sp, sizeof(shutmsg));
305 }
306 while (*myaclbuf && *myaclbuf++ != '\n');
307 }
308
309 /* three cases
310 * -- now
311 * -- +ddd
312 * -- HHMM
313 */
314
315 c = -1;
316
317 if (optind < argc) {
318 if (!strcasecmp(argv[optind], "now")) {
319 c_time = time(0);
320 tp = localtime(&c_time);
321 }
322 else if ((*(argv[optind])) == '+') {
323 c_time = time(0);
324 c_time += 60 * atoi(++(argv[optind]));
325 tp = localtime(&c_time);
326 }
327 else if ((c = atoi(argv[optind])) >= 0) {
328 c_time = time(0);
329 tp = localtime(&c_time);
330 tp->tm_hour = c / 100;
331 tp->tm_min = c % 100;
332
333 if ((tp->tm_hour > 23) || (tp->tm_min > 59)) {
334 fprintf(stderr, "%s: illegal time format.\n", progname);
335 exit(1);
336 }
337 }
338 }
339 if (c_time <= 0) {
340 usage(1);
341 }
342
343 if (sp == NULL) {
344 fprintf(stderr, "%s: no shutdown path defined in ftpaccess file %s.\n",
345 progname, _PATH_FTPACCESS);
346 exit(1);
347 }
348
349 /* do we have a shutdown message? */
350 if (++optind < argc)
351 (void) strlcpy(buf, argv[optind++], sizeof(buf));
352 else
353 (void) strlcpy(buf, message, sizeof(buf));
354
355 massage(buf);
356
357 /*
358 ** Create the system shutdown message file at the location
359 ** specified in the ftpaccess 'shutdown' directive. This
360 ** is for support of real system users.
361 */
362 c = shutdown_msgfile(shutmsg, buf);
363 msgfiles[numfiles++] = shutmsg;
364
365 /*
366 ** Determine if the site supports anonymous ftp and if so, create
367 ** the shutdown message file in the anonymous ftp area as well
368 ** so that shutdown works appropriately for both real and guest
369 ** accounts. Save in msgfiles array for later comparison.
370 */
371
372 if ((pwent = getpwnam("ftp")) != NULL) {
373 (void) snprintf(anonpath, sizeof(anonpath), "%s%s", pwent->pw_dir,
374 shutmsg);
375 if (newfile(anonpath))
376 c += shutdown_msgfile(anonpath, buf);
377 }
378
379 #ifdef VIRTUAL
380 /*
381 ** Search the Master access file for virtual ftp servers.
382 ** If found, construct a path to the shutdown message file
383 ** under the virtual server's root. Don't duplicate what
384 ** is specified in the "ftp" account directory information.
385 */
386
387 rewind(accessfile);
388
389 while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) {
390 if (strncasecmp(linebuf, "virtual", 7) == 0) {
391
392 if ((sp = strstr(linebuf, "root")) != NULL) {
393 if ((cp = strchr(sp, '\n')) != NULL)
394 *cp = '\0'; /* strip newline */
395
396 sp += 4; /* skip past "root" keyword */
397
398 while (*sp && isspace(*sp)) /* skip whitespace to root path */
399 sp++;
400 cp = sp;
401 while (*sp && !isspace(*sp))
402 sp++;
403 *sp = '\0'; /* truncate blanks, comments etc. */
404
405 (void) snprintf(altmsgpath, sizeof(altmsgpath), "%s%s", cp,
406 shutmsg);
407
408 if (newfile(altmsgpath))
409 c += shutdown_msgfile(altmsgpath, buf);
410 }
411 }
412 }
413
414 /*
415 ** Need to deal with the access files at the virtual domain directory
416 ** locations specified in the ftpservers file.
417 */
418
419 if ((svrfp = fopen(_PATH_FTPSERVERS, "r")) != NULL) {
420 while (read_servers_line(svrfp, hostaddress, sizeof(hostaddress),
421 configdir, sizeof(configdir)) == 1) {
422 /* get rid of any trailing slash */
423 sp = configdir + (strlen(configdir) - 1);
424 if (*sp == '/')
425 *sp = '\0';
426
427 /*
428 ** check to see that a valid directory value was
429 ** supplied and not something such as "INTERNAL"
430 **
431 ** It is valid to have a string such as "INTERNAL" in the
432 ** ftpservers entry. This is not an error. Silently ignore it.
433 */
434
435 if ((stat(configdir, &finfo) < 0) ||
436 ((finfo.st_mode & S_IFMT) != S_IFDIR))
437 continue;
438
439 (void) snprintf(accesspath, sizeof(accesspath), "%s/ftpaccess",
440 configdir);
441
442 (void) fclose(accessfile);
443
444 if ((accessfile = fopen(accesspath, "r")) == NULL) {
445 if (errno != ENOENT) {
446 fprintf(stderr, "%s: could not open access file %s: %s\n",
447 progname, accesspath, strerror(errno));
448 continue;
449 }
450 }
451
452 /* need to find the root path */
453
454 while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) {
455 if ((sp = strstr(linebuf, "root")) != NULL) {
456 if ((cp = strchr(sp, '\n')) != NULL)
457 *cp = '\0'; /* strip newline */
458 sp += 4; /* skip past "root" keyword */
459
460 while (*sp && isspace(*sp)) /* skip whitespace to path */
461 sp++;
462 cp = sp;
463 while (*sp && !isspace(*sp))
464 sp++;
465 *sp = '\0'; /* truncate blanks, comments etc. */
466 (void) strlcpy(root, cp, sizeof(root));
467 break;
468 }
469 }
470 /* need to find the shutdown message file path */
471
472 rewind(accessfile);
473
474 while (fgets(linebuf, sizeof(linebuf) - 1, accessfile) != NULL) {
475 if ((sp = strstr(linebuf, "shutdown")) != NULL) {
476 if ((cp = strchr(sp, '\n')) != NULL)
477 *cp = '\0'; /* strip newline */
478 sp += 8; /* skip past "root" keyword */
479
480 while (*sp && isspace(*sp)) /* skip whitespace to path */
481 sp++;
482 cp = sp;
483 while (*sp && !isspace(*sp))
484 sp++;
485 *sp = '\0'; /* truncate blanks, comments etc. */
486 break;
487 }
488 }
489
490 /*
491 ** check to make sure the admin hasn't specified
492 ** a complete path in the 'shutdown' directive.
493 */
494 if ((sp = strstr(cp, root)) == NULL)
495 (void) snprintf(altmsgpath, sizeof(altmsgpath), "%s%s", root,
496 cp);
497
498 /*
499 ** Check to see if the message file has been created elsewhere.
500 */
501 if (newfile(altmsgpath))
502 c += shutdown_msgfile(altmsgpath, buf);
503 }
504 fclose(svrfp);
505 }
506 #endif /* VIRTUAL */
507
508 fclose(accessfile);
509 free(aclbuf);
510 exit(c > 0 ? 1 : 0);
511 }