1*cb93dc63Sshm /* $NetBSD: conf.c,v 1.65 2023/09/29 14:49:03 shm Exp $ */
231547ec6Slukem
331547ec6Slukem /*-
40053962fSlukem * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
531547ec6Slukem * All rights reserved.
631547ec6Slukem *
731547ec6Slukem * This code is derived from software contributed to The NetBSD Foundation
831547ec6Slukem * by Simon Burge and Luke Mewburn.
931547ec6Slukem *
1031547ec6Slukem * Redistribution and use in source and binary forms, with or without
1131547ec6Slukem * modification, are permitted provided that the following conditions
1231547ec6Slukem * are met:
1331547ec6Slukem * 1. Redistributions of source code must retain the above copyright
1431547ec6Slukem * notice, this list of conditions and the following disclaimer.
1531547ec6Slukem * 2. Redistributions in binary form must reproduce the above copyright
1631547ec6Slukem * notice, this list of conditions and the following disclaimer in the
1731547ec6Slukem * documentation and/or other materials provided with the distribution.
1831547ec6Slukem *
1931547ec6Slukem * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2031547ec6Slukem * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2131547ec6Slukem * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22a1582495Sjtc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23a1582495Sjtc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2431547ec6Slukem * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2531547ec6Slukem * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2631547ec6Slukem * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2731547ec6Slukem * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2831547ec6Slukem * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2931547ec6Slukem * POSSIBILITY OF SUCH DAMAGE.
3031547ec6Slukem */
3131547ec6Slukem
322424c4f9Schristos #include <sys/cdefs.h>
3331547ec6Slukem #ifndef lint
34*cb93dc63Sshm __RCSID("$NetBSD: conf.c,v 1.65 2023/09/29 14:49:03 shm Exp $");
3531547ec6Slukem #endif /* not lint */
3631547ec6Slukem
3731547ec6Slukem #include <sys/types.h>
3831547ec6Slukem #include <sys/param.h>
3950150481Slukem #include <sys/socket.h>
4031547ec6Slukem #include <sys/stat.h>
4131547ec6Slukem
4221d03121Slukem #include <ctype.h>
4331547ec6Slukem #include <errno.h>
4416e88612Slukem #include <fcntl.h>
45bef47ea9Schristos #include <pwd.h>
4631547ec6Slukem #include <glob.h>
4750150481Slukem #include <netdb.h>
4816e88612Slukem #include <signal.h>
4931547ec6Slukem #include <stdio.h>
502424c4f9Schristos #include <stdlib.h>
5131547ec6Slukem #include <string.h>
5231547ec6Slukem #include <stringlist.h>
5331547ec6Slukem #include <syslog.h>
54397e2cfcSlukem #include <time.h>
55397e2cfcSlukem #include <unistd.h>
56397e2cfcSlukem #include <util.h>
5731547ec6Slukem
58b2f939acSexplorer #ifdef KERBEROS5
59cee9ac24Schristos #include <krb5/krb5.h>
60b2f939acSexplorer #endif
61b2f939acSexplorer
6231547ec6Slukem #include "extern.h"
6331547ec6Slukem #include "pathnames.h"
6431547ec6Slukem
65dcc88422Slukem static char *strend(const char *, char *);
66dcc88422Slukem static int filetypematch(char *, int);
6731547ec6Slukem
680e5bdd51Slukem
6950150481Slukem /* class defaults */
7050150481Slukem #define DEFAULT_LIMIT -1 /* unlimited connections */
7150150481Slukem #define DEFAULT_MAXFILESIZE -1 /* unlimited file size */
7250150481Slukem #define DEFAULT_MAXTIMEOUT 7200 /* 2 hours */
7350150481Slukem #define DEFAULT_TIMEOUT 900 /* 15 minutes */
747ea2be42Slukem #define DEFAULT_UMASK 027 /* rw-r----- */
7550150481Slukem
7631547ec6Slukem /*
7716e88612Slukem * Initialise curclass to an `empty' state
7816e88612Slukem */
7916e88612Slukem void
init_curclass(void)80dcc88422Slukem init_curclass(void)
8116e88612Slukem {
8216e88612Slukem struct ftpconv *conv, *cnext;
8316e88612Slukem
8416e88612Slukem for (conv = curclass.conversions; conv != NULL; conv = cnext) {
8516e88612Slukem REASSIGN(conv->suffix, NULL);
8616e88612Slukem REASSIGN(conv->types, NULL);
8716e88612Slukem REASSIGN(conv->disable, NULL);
8816e88612Slukem REASSIGN(conv->command, NULL);
8916e88612Slukem cnext = conv->next;
9016e88612Slukem free(conv);
9116e88612Slukem }
92c8493e94Slukem
9350150481Slukem memset((char *)&curclass.advertise, 0, sizeof(curclass.advertise));
9450150481Slukem curclass.advertise.su_len = 0; /* `not used' */
95e3a2c5ccSlukem REASSIGN(curclass.chroot, NULL);
9616e88612Slukem REASSIGN(curclass.classname, NULL);
9716e88612Slukem curclass.conversions = NULL;
9816e88612Slukem REASSIGN(curclass.display, NULL);
99e3a2c5ccSlukem REASSIGN(curclass.homedir, NULL);
10050150481Slukem curclass.limit = DEFAULT_LIMIT;
10116e88612Slukem REASSIGN(curclass.limitfile, NULL);
10250150481Slukem curclass.maxfilesize = DEFAULT_MAXFILESIZE;
10316e88612Slukem curclass.maxrateget = 0;
10416e88612Slukem curclass.maxrateput = 0;
10550150481Slukem curclass.maxtimeout = DEFAULT_MAXTIMEOUT;
1061edbda9aSchristos REASSIGN(curclass.motd, ftpd_strdup(_NAME_FTPLOGINMESG));
10716e88612Slukem REASSIGN(curclass.notify, NULL);
108c8493e94Slukem curclass.portmin = 0;
109c8493e94Slukem curclass.portmax = 0;
11016e88612Slukem curclass.rateget = 0;
11116e88612Slukem curclass.rateput = 0;
11250150481Slukem curclass.timeout = DEFAULT_TIMEOUT;
113e3a2c5ccSlukem /* curclass.type is set elsewhere */
11450150481Slukem curclass.umask = DEFAULT_UMASK;
115260e9f55Senami curclass.mmapsize = 0;
116260e9f55Senami curclass.readsize = 0;
117260e9f55Senami curclass.writesize = 0;
118260e9f55Senami curclass.sendbufsize = 0;
119260e9f55Senami curclass.sendlowat = 0;
120999fd3d6Slukem
121999fd3d6Slukem CURCLASS_FLAGS_SET(checkportcmd);
122c31e16f7Slukem CURCLASS_FLAGS_CLR(denyquick);
123f0b52873Sginsbach CURCLASS_FLAGS_CLR(hidesymlinks);
124999fd3d6Slukem CURCLASS_FLAGS_SET(modify);
125999fd3d6Slukem CURCLASS_FLAGS_SET(passive);
126c31e16f7Slukem CURCLASS_FLAGS_CLR(private);
127999fd3d6Slukem CURCLASS_FLAGS_CLR(sanenames);
128999fd3d6Slukem CURCLASS_FLAGS_SET(upload);
12916e88612Slukem }
13016e88612Slukem
13116e88612Slukem /*
13231547ec6Slukem * Parse the configuration file, looking for the named class, and
13331547ec6Slukem * define curclass to contain the appropriate settings.
13431547ec6Slukem */
13531547ec6Slukem void
parse_conf(const char * findclass)136dcc88422Slukem parse_conf(const char *findclass)
13731547ec6Slukem {
13831547ec6Slukem FILE *f;
13931547ec6Slukem char *buf, *p;
14031547ec6Slukem size_t len;
141999fd3d6Slukem LLT llval;
142999fd3d6Slukem int none, match;
143ee2d1afbSlukem char *endp, errbuf[100];
1447e803788Slukem char *class, *word, *arg, *template;
14531547ec6Slukem const char *infile;
146397e2cfcSlukem size_t line;
14731547ec6Slukem struct ftpconv *conv, *cnext;
14831547ec6Slukem
14916e88612Slukem init_curclass();
1501edbda9aSchristos REASSIGN(curclass.classname, ftpd_strdup(findclass));
15150150481Slukem /* set more guest defaults */
15231547ec6Slukem if (strcasecmp(findclass, "guest") == 0) {
153999fd3d6Slukem CURCLASS_FLAGS_CLR(modify);
15483955f6aSlukem curclass.umask = 0707;
15531547ec6Slukem }
15631547ec6Slukem
157d465dbd4Schristos infile = conffilename(_NAME_FTPDCONF);
15831547ec6Slukem if ((f = fopen(infile, "r")) == NULL)
15931547ec6Slukem return;
16031547ec6Slukem
16131547ec6Slukem line = 0;
1627e803788Slukem template = NULL;
163397e2cfcSlukem for (;
164397e2cfcSlukem (buf = fparseln(f, &len, &line, NULL, FPARSELN_UNESCCOMM |
165397e2cfcSlukem FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) != NULL;
166397e2cfcSlukem free(buf)) {
16731547ec6Slukem none = match = 0;
168397e2cfcSlukem p = buf;
1690bba8ce3Slukem if (len < 1)
1700bba8ce3Slukem continue;
171397e2cfcSlukem if (p[len - 1] == '\n')
172397e2cfcSlukem p[--len] = '\0';
173397e2cfcSlukem if (EMPTYSTR(p))
17431547ec6Slukem continue;
17531547ec6Slukem
176397e2cfcSlukem NEXTWORD(p, word);
177397e2cfcSlukem NEXTWORD(p, class);
178397e2cfcSlukem NEXTWORD(p, arg);
17931547ec6Slukem if (EMPTYSTR(word) || EMPTYSTR(class))
18031547ec6Slukem continue;
18131547ec6Slukem if (strcasecmp(class, "none") == 0)
18231547ec6Slukem none = 1;
183559037c2Slukem if (! (strcasecmp(class, findclass) == 0 ||
184559037c2Slukem (template != NULL && strcasecmp(class, template) == 0) ||
185559037c2Slukem none ||
186559037c2Slukem strcasecmp(class, "all") == 0) )
18731547ec6Slukem continue;
18831547ec6Slukem
189ee2d1afbSlukem #define CONF_FLAG(Field) \
190999fd3d6Slukem do { \
191999fd3d6Slukem if (none || \
192999fd3d6Slukem (!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) \
193ee2d1afbSlukem CURCLASS_FLAGS_CLR(Field); \
194999fd3d6Slukem else \
195ee2d1afbSlukem CURCLASS_FLAGS_SET(Field); \
196999fd3d6Slukem } while (0)
197999fd3d6Slukem
198ee2d1afbSlukem #define CONF_STRING(Field) \
199999fd3d6Slukem do { \
200999fd3d6Slukem if (none || EMPTYSTR(arg)) \
201999fd3d6Slukem arg = NULL; \
202999fd3d6Slukem else \
2031edbda9aSchristos arg = ftpd_strdup(arg); \
204ee2d1afbSlukem REASSIGN(curclass.Field, arg); \
205999fd3d6Slukem } while (0)
206999fd3d6Slukem
207ee2d1afbSlukem #define CONF_LL(Field,Arg,Min,Max) \
208ec6387d5Saidan do { \
209ee2d1afbSlukem if (none || EMPTYSTR(Arg)) \
210ee2d1afbSlukem goto nextline; \
211ee2d1afbSlukem llval = strsuftollx(#Field, Arg, Min, Max, \
212ee2d1afbSlukem errbuf, sizeof(errbuf)); \
213ee2d1afbSlukem if (errbuf[0]) { \
214ee2d1afbSlukem syslog(LOG_WARNING, "%s line %d: %s", \
215ee2d1afbSlukem infile, (int)line, errbuf); \
216ee2d1afbSlukem goto nextline; \
217ec6387d5Saidan } \
218ee2d1afbSlukem curclass.Field = llval; \
219ec6387d5Saidan } while(0)
22050150481Slukem
22150150481Slukem if (0) {
22250150481Slukem /* no-op */
22350150481Slukem
224afa0abb8Slukem } else if ((strcasecmp(word, "advertise") == 0)
225afa0abb8Slukem || (strcasecmp(word, "advertize") == 0)) {
22650150481Slukem struct addrinfo hints, *res;
22750150481Slukem int error;
22850150481Slukem
22950150481Slukem memset((char *)&curclass.advertise, 0,
23050150481Slukem sizeof(curclass.advertise));
23150150481Slukem curclass.advertise.su_len = 0;
23250150481Slukem if (none || EMPTYSTR(arg))
23350150481Slukem continue;
23450150481Slukem res = NULL;
23550150481Slukem memset(&hints, 0, sizeof(hints));
23650150481Slukem /*
23750150481Slukem * only get addresses of the family
23850150481Slukem * that we're listening on
23950150481Slukem */
24050150481Slukem hints.ai_family = ctrl_addr.su_family;
24150150481Slukem hints.ai_socktype = SOCK_STREAM;
24250150481Slukem error = getaddrinfo(arg, "0", &hints, &res);
24350150481Slukem if (error) {
24450150481Slukem syslog(LOG_WARNING, "%s line %d: %s",
24550150481Slukem infile, (int)line, gai_strerror(error));
24650150481Slukem advertiseparsefail:
24750150481Slukem if (res)
24850150481Slukem freeaddrinfo(res);
24950150481Slukem continue;
25050150481Slukem }
25150150481Slukem if (res->ai_next) {
25250150481Slukem syslog(LOG_WARNING,
25350150481Slukem "%s line %d: multiple addresses returned for `%s'; please be more specific",
25450150481Slukem infile, (int)line, arg);
25550150481Slukem goto advertiseparsefail;
25650150481Slukem }
25750150481Slukem if (sizeof(curclass.advertise) < res->ai_addrlen || (
25850150481Slukem #ifdef INET6
25950150481Slukem res->ai_family != AF_INET6 &&
26050150481Slukem #endif
26150150481Slukem res->ai_family != AF_INET)) {
26250150481Slukem syslog(LOG_WARNING,
26350150481Slukem "%s line %d: unsupported protocol %d for `%s'",
26450150481Slukem infile, (int)line, res->ai_family, arg);
26550150481Slukem goto advertiseparsefail;
26650150481Slukem }
26750150481Slukem memcpy(&curclass.advertise, res->ai_addr,
26850150481Slukem res->ai_addrlen);
26950150481Slukem curclass.advertise.su_len = res->ai_addrlen;
27050150481Slukem freeaddrinfo(res);
27150150481Slukem
27250150481Slukem } else if (strcasecmp(word, "checkportcmd") == 0) {
273999fd3d6Slukem CONF_FLAG(checkportcmd);
274397e2cfcSlukem
275e3a2c5ccSlukem } else if (strcasecmp(word, "chroot") == 0) {
276999fd3d6Slukem CONF_STRING(chroot);
277e3a2c5ccSlukem
27821d03121Slukem } else if (strcasecmp(word, "classtype") == 0) {
27921d03121Slukem if (!none && !EMPTYSTR(arg)) {
28021d03121Slukem if (strcasecmp(arg, "GUEST") == 0)
28121d03121Slukem curclass.type = CLASS_GUEST;
28221d03121Slukem else if (strcasecmp(arg, "CHROOT") == 0)
28321d03121Slukem curclass.type = CLASS_CHROOT;
28421d03121Slukem else if (strcasecmp(arg, "REAL") == 0)
28521d03121Slukem curclass.type = CLASS_REAL;
28621d03121Slukem else {
28721d03121Slukem syslog(LOG_WARNING,
28821d03121Slukem "%s line %d: unknown class type `%s'",
28921d03121Slukem infile, (int)line, arg);
29021d03121Slukem continue;
29121d03121Slukem }
29221d03121Slukem }
29321d03121Slukem
29483955f6aSlukem } else if (strcasecmp(word, "conversion") == 0) {
2950bba8ce3Slukem char *suffix, *types, *disable, *convcmd;
2960bba8ce3Slukem
29731547ec6Slukem if (EMPTYSTR(arg)) {
29831547ec6Slukem syslog(LOG_WARNING,
29931547ec6Slukem "%s line %d: %s requires a suffix",
300397e2cfcSlukem infile, (int)line, word);
30131547ec6Slukem continue; /* need a suffix */
30231547ec6Slukem }
303397e2cfcSlukem NEXTWORD(p, types);
304397e2cfcSlukem NEXTWORD(p, disable);
305397e2cfcSlukem convcmd = p;
30631547ec6Slukem if (convcmd)
30731547ec6Slukem convcmd += strspn(convcmd, " \t");
3081edbda9aSchristos suffix = ftpd_strdup(arg);
30931547ec6Slukem if (none || EMPTYSTR(types) ||
31031547ec6Slukem EMPTYSTR(disable) || EMPTYSTR(convcmd)) {
31131547ec6Slukem types = NULL;
31231547ec6Slukem disable = NULL;
31331547ec6Slukem convcmd = NULL;
31431547ec6Slukem } else {
3151edbda9aSchristos types = ftpd_strdup(types);
3161edbda9aSchristos disable = ftpd_strdup(disable);
3171edbda9aSchristos convcmd = ftpd_strdup(convcmd);
31831547ec6Slukem }
31931547ec6Slukem for (conv = curclass.conversions; conv != NULL;
32031547ec6Slukem conv = conv->next) {
3210bba8ce3Slukem if (strcmp(conv->suffix, suffix) == 0)
32231547ec6Slukem break;
32331547ec6Slukem }
32431547ec6Slukem if (conv == NULL) {
32531547ec6Slukem conv = (struct ftpconv *)
32631547ec6Slukem calloc(1, sizeof(struct ftpconv));
32731547ec6Slukem if (conv == NULL) {
32831547ec6Slukem syslog(LOG_WARNING, "can't malloc");
32931547ec6Slukem continue;
33031547ec6Slukem }
331397e2cfcSlukem conv->next = NULL;
332397e2cfcSlukem for (cnext = curclass.conversions;
333397e2cfcSlukem cnext != NULL; cnext = cnext->next)
334397e2cfcSlukem if (cnext->next == NULL)
335397e2cfcSlukem break;
336397e2cfcSlukem if (cnext != NULL)
337397e2cfcSlukem cnext->next = conv;
338397e2cfcSlukem else
33931547ec6Slukem curclass.conversions = conv;
34031547ec6Slukem }
3410bba8ce3Slukem REASSIGN(conv->suffix, suffix);
34231547ec6Slukem REASSIGN(conv->types, types);
34331547ec6Slukem REASSIGN(conv->disable, disable);
34431547ec6Slukem REASSIGN(conv->command, convcmd);
345397e2cfcSlukem
346c31e16f7Slukem } else if (strcasecmp(word, "denyquick") == 0) {
347c31e16f7Slukem CONF_FLAG(denyquick);
348c31e16f7Slukem
34931547ec6Slukem } else if (strcasecmp(word, "display") == 0) {
350999fd3d6Slukem CONF_STRING(display);
351397e2cfcSlukem
352f0b52873Sginsbach } else if (strcasecmp(word, "hidesymlinks") == 0) {
353f0b52873Sginsbach CONF_FLAG(hidesymlinks);
354f0b52873Sginsbach
355e3a2c5ccSlukem } else if (strcasecmp(word, "homedir") == 0) {
356999fd3d6Slukem CONF_STRING(homedir);
357999fd3d6Slukem
35816e88612Slukem } else if (strcasecmp(word, "limit") == 0) {
35950150481Slukem curclass.limit = DEFAULT_LIMIT;
36050150481Slukem REASSIGN(curclass.limitfile, NULL);
361ee2d1afbSlukem CONF_LL(limit, arg, -1, LLTMAX);
3627e803788Slukem REASSIGN(curclass.limitfile,
3631edbda9aSchristos EMPTYSTR(p) ? NULL : ftpd_strdup(p));
36416e88612Slukem
36550150481Slukem } else if (strcasecmp(word, "maxfilesize") == 0) {
36650150481Slukem curclass.maxfilesize = DEFAULT_MAXFILESIZE;
367ee2d1afbSlukem CONF_LL(maxfilesize, arg, -1, LLTMAX);
36850150481Slukem
36931547ec6Slukem } else if (strcasecmp(word, "maxtimeout") == 0) {
37050150481Slukem curclass.maxtimeout = DEFAULT_MAXTIMEOUT;
371ee2d1afbSlukem CONF_LL(maxtimeout, arg,
372ee2d1afbSlukem MIN(30, curclass.timeout), LLTMAX);
373397e2cfcSlukem
374260e9f55Senami } else if (strcasecmp(word, "mmapsize") == 0) {
375260e9f55Senami curclass.mmapsize = 0;
3760053962fSlukem CONF_LL(mmapsize, arg, 0, SSIZE_MAX);
377260e9f55Senami
378260e9f55Senami } else if (strcasecmp(word, "readsize") == 0) {
379260e9f55Senami curclass.readsize = 0;
3800053962fSlukem CONF_LL(readsize, arg, 0, SSIZE_MAX);
381260e9f55Senami
382260e9f55Senami } else if (strcasecmp(word, "writesize") == 0) {
383260e9f55Senami curclass.writesize = 0;
3840053962fSlukem CONF_LL(writesize, arg, 0, SSIZE_MAX);
385260e9f55Senami
386818f7caaSginsbach } else if (strcasecmp(word, "recvbufsize") == 0) {
387818f7caaSginsbach curclass.recvbufsize = 0;
3880053962fSlukem CONF_LL(recvbufsize, arg, 0, INT_MAX);
389818f7caaSginsbach
390260e9f55Senami } else if (strcasecmp(word, "sendbufsize") == 0) {
391260e9f55Senami curclass.sendbufsize = 0;
3920053962fSlukem CONF_LL(sendbufsize, arg, 0, INT_MAX);
393260e9f55Senami
394260e9f55Senami } else if (strcasecmp(word, "sendlowat") == 0) {
395260e9f55Senami curclass.sendlowat = 0;
3960053962fSlukem CONF_LL(sendlowat, arg, 0, INT_MAX);
397260e9f55Senami
39831547ec6Slukem } else if (strcasecmp(word, "modify") == 0) {
399999fd3d6Slukem CONF_FLAG(modify);
400397e2cfcSlukem
40121d03121Slukem } else if (strcasecmp(word, "motd") == 0) {
402999fd3d6Slukem CONF_STRING(motd);
40321d03121Slukem
40431547ec6Slukem } else if (strcasecmp(word, "notify") == 0) {
405999fd3d6Slukem CONF_STRING(notify);
406397e2cfcSlukem
407e3ab2046Stv } else if (strcasecmp(word, "passive") == 0) {
408999fd3d6Slukem CONF_FLAG(passive);
409397e2cfcSlukem
410c8493e94Slukem } else if (strcasecmp(word, "portrange") == 0) {
41192ebc577Sitojun long minport, maxport;
412c8493e94Slukem
413c8493e94Slukem curclass.portmin = 0;
414c8493e94Slukem curclass.portmax = 0;
41550150481Slukem if (none || EMPTYSTR(arg))
416c8493e94Slukem continue;
417ee2d1afbSlukem if (EMPTYSTR(p)) {
418c8493e94Slukem syslog(LOG_WARNING,
419c8493e94Slukem "%s line %d: missing maxport argument",
420c8493e94Slukem infile, (int)line);
421c8493e94Slukem continue;
422c8493e94Slukem }
423ee2d1afbSlukem minport = strsuftollx("minport", arg, IPPORT_RESERVED,
424ee2d1afbSlukem IPPORT_ANONMAX, errbuf, sizeof(errbuf));
425ee2d1afbSlukem if (errbuf[0]) {
426ee2d1afbSlukem syslog(LOG_WARNING, "%s line %d: %s",
427ee2d1afbSlukem infile, (int)line, errbuf);
428c8493e94Slukem continue;
429c8493e94Slukem }
430ee2d1afbSlukem maxport = strsuftollx("maxport", p, IPPORT_RESERVED,
431ee2d1afbSlukem IPPORT_ANONMAX, errbuf, sizeof(errbuf));
432ee2d1afbSlukem if (errbuf[0]) {
433ee2d1afbSlukem syslog(LOG_WARNING, "%s line %d: %s",
434ee2d1afbSlukem infile, (int)line, errbuf);
435c8493e94Slukem continue;
436c8493e94Slukem }
437c8493e94Slukem if (minport >= maxport) {
438c8493e94Slukem syslog(LOG_WARNING,
43992ebc577Sitojun "%s line %d: minport %ld >= maxport %ld",
440c8493e94Slukem infile, (int)line, minport, maxport);
441c8493e94Slukem continue;
442c8493e94Slukem }
44392ebc577Sitojun curclass.portmin = (int)minport;
44492ebc577Sitojun curclass.portmax = (int)maxport;
445c8493e94Slukem
446c31e16f7Slukem } else if (strcasecmp(word, "private") == 0) {
447c31e16f7Slukem CONF_FLAG(private);
448c31e16f7Slukem
44921d03121Slukem } else if (strcasecmp(word, "rateget") == 0) {
450ee2d1afbSlukem curclass.maxrateget = curclass.rateget = 0;
451ee2d1afbSlukem CONF_LL(rateget, arg, 0, LLTMAX);
452ee2d1afbSlukem curclass.maxrateget = curclass.rateget;
45321d03121Slukem
45421d03121Slukem } else if (strcasecmp(word, "rateput") == 0) {
455ee2d1afbSlukem curclass.maxrateput = curclass.rateput = 0;
456ee2d1afbSlukem CONF_LL(rateput, arg, 0, LLTMAX);
457ee2d1afbSlukem curclass.maxrateput = curclass.rateput;
458999fd3d6Slukem
459999fd3d6Slukem } else if (strcasecmp(word, "sanenames") == 0) {
460999fd3d6Slukem CONF_FLAG(sanenames);
46121d03121Slukem
46231547ec6Slukem } else if (strcasecmp(word, "timeout") == 0) {
46350150481Slukem curclass.timeout = DEFAULT_TIMEOUT;
464ee2d1afbSlukem CONF_LL(timeout, arg, 30, curclass.maxtimeout);
465397e2cfcSlukem
4667e803788Slukem } else if (strcasecmp(word, "template") == 0) {
4677e803788Slukem if (none)
4687e803788Slukem continue;
4691edbda9aSchristos REASSIGN(template, EMPTYSTR(arg) ? NULL : ftpd_strdup(arg));
4707e803788Slukem
47131547ec6Slukem } else if (strcasecmp(word, "umask") == 0) {
472ef70558fSlukem unsigned long fumask;
47331547ec6Slukem
47450150481Slukem curclass.umask = DEFAULT_UMASK;
47531547ec6Slukem if (none || EMPTYSTR(arg))
47631547ec6Slukem continue;
47792ebc577Sitojun errno = 0;
47892ebc577Sitojun endp = NULL;
47992ebc577Sitojun fumask = strtoul(arg, &endp, 8);
48092ebc577Sitojun if (errno || *arg == '\0' || *endp != '\0' ||
48192ebc577Sitojun fumask > 0777) {
48231547ec6Slukem syslog(LOG_WARNING,
48331547ec6Slukem "%s line %d: invalid umask %s",
484397e2cfcSlukem infile, (int)line, arg);
48531547ec6Slukem continue;
48631547ec6Slukem }
48792ebc577Sitojun curclass.umask = (mode_t)fumask;
488397e2cfcSlukem
48921d03121Slukem } else if (strcasecmp(word, "upload") == 0) {
490999fd3d6Slukem CONF_FLAG(upload);
491999fd3d6Slukem if (! CURCLASS_FLAGS_ISSET(upload))
492999fd3d6Slukem CURCLASS_FLAGS_CLR(modify);
49321d03121Slukem
49431547ec6Slukem } else {
49531547ec6Slukem syslog(LOG_WARNING,
49631547ec6Slukem "%s line %d: unknown directive '%s'",
497397e2cfcSlukem infile, (int)line, word);
49831547ec6Slukem continue;
49931547ec6Slukem }
500ee2d1afbSlukem nextline:
501ee2d1afbSlukem ;
50231547ec6Slukem }
5037e803788Slukem REASSIGN(template, NULL);
50431547ec6Slukem fclose(f);
50531547ec6Slukem }
50631547ec6Slukem
50731547ec6Slukem /*
50831547ec6Slukem * Show file listed in curclass.display first time in, and list all the
50950150481Slukem * files named in curclass.notify in the current directory.
51050150481Slukem * Send back responses with the prefix `code' + "-".
51150150481Slukem * If code == -1, flush the internal cache of directory names and return.
51231547ec6Slukem */
51331547ec6Slukem void
show_chdir_messages(int code)514dcc88422Slukem show_chdir_messages(int code)
51531547ec6Slukem {
51631547ec6Slukem static StringList *slist = NULL;
51731547ec6Slukem
51831547ec6Slukem struct stat st;
51931547ec6Slukem struct tm *t;
52031547ec6Slukem glob_t gl;
52131547ec6Slukem time_t now, then;
52231547ec6Slukem int age;
5233a491edaSlukem char curwd[MAXPATHLEN];
52431547ec6Slukem char *cp, **rlist;
52531547ec6Slukem
52650150481Slukem if (code == -1) {
52750150481Slukem if (slist != NULL)
52850150481Slukem sl_free(slist, 1);
52950150481Slukem slist = NULL;
53050150481Slukem return;
53150150481Slukem }
53250150481Slukem
533ab88a150Slukem if (quietmessages)
534ab88a150Slukem return;
535ab88a150Slukem
53631547ec6Slukem /* Setup list for directory cache */
53731547ec6Slukem if (slist == NULL)
53831547ec6Slukem slist = sl_init();
539d51504eeSlukem if (slist == NULL) {
540d51504eeSlukem syslog(LOG_WARNING, "can't allocate memory for stringlist");
541d51504eeSlukem return;
542d51504eeSlukem }
54331547ec6Slukem
54431547ec6Slukem /* Check if this directory has already been visited */
5453a491edaSlukem if (getcwd(curwd, sizeof(curwd) - 1) == NULL) {
5467ccec6acSmouse syslog(LOG_WARNING, "can't getcwd: %s", strerror(errno));
54731547ec6Slukem return;
54831547ec6Slukem }
5493a491edaSlukem if (sl_find(slist, curwd) != NULL)
55031547ec6Slukem return;
55131547ec6Slukem
5521edbda9aSchristos cp = ftpd_strdup(curwd);
553d51504eeSlukem if (sl_add(slist, cp) == -1)
554d51504eeSlukem syslog(LOG_WARNING, "can't add `%s' to stringlist", cp);
55531547ec6Slukem
55631547ec6Slukem /* First check for a display file */
557e3a2c5ccSlukem (void)display_file(curclass.display, code);
55831547ec6Slukem
55931547ec6Slukem /* Now see if there are any notify files */
56021d03121Slukem if (EMPTYSTR(curclass.notify))
56131547ec6Slukem return;
56231547ec6Slukem
56353c91d8fSlukem memset(&gl, 0, sizeof(gl));
564adbaddc9Slukem if (glob(curclass.notify, GLOB_BRACE|GLOB_LIMIT, NULL, &gl) != 0
56555dd4165Schristos || gl.gl_matchc == 0) {
56655dd4165Schristos globfree(&gl);
56731547ec6Slukem return;
56855dd4165Schristos }
56931547ec6Slukem time(&now);
57031547ec6Slukem for (rlist = gl.gl_pathv; *rlist != NULL; rlist++) {
57131547ec6Slukem if (stat(*rlist, &st) != 0)
57231547ec6Slukem continue;
5733ff72472Smycroft if (!S_ISREG(st.st_mode))
57431547ec6Slukem continue;
57531547ec6Slukem then = st.st_mtime;
5764b2b2847Slukem if (code != 0) {
577eac5778eSsommerfeld reply(-code, "%s", "");
5784b2b2847Slukem code = 0;
5794b2b2847Slukem }
58073f082e2Slukem reply(-code, "Please read the file %s", *rlist);
58131547ec6Slukem t = localtime(&now);
58231547ec6Slukem age = 365 * t->tm_year + t->tm_yday;
58331547ec6Slukem t = localtime(&then);
58431547ec6Slukem age -= 365 * t->tm_year + t->tm_yday;
58573f082e2Slukem reply(-code, " it was last modified on %.24s - %d day%s ago",
58625cf35a4Slukem ctime(&then), age, PLURAL(age));
58731547ec6Slukem }
58831547ec6Slukem globfree(&gl);
58931547ec6Slukem }
59031547ec6Slukem
59121d03121Slukem int
display_file(const char * file,int code)592e3a2c5ccSlukem display_file(const char *file, int code)
59321d03121Slukem {
59421d03121Slukem FILE *f;
5951571b4e9Skristerw char *buf, *p;
5963a491edaSlukem char curwd[MAXPATHLEN];
59721d03121Slukem size_t len;
598999fd3d6Slukem off_t lastnum;
59921d03121Slukem time_t now;
60021d03121Slukem
601999fd3d6Slukem lastnum = 0;
602ab88a150Slukem if (quietmessages)
603ab88a150Slukem return (0);
604ab88a150Slukem
60521d03121Slukem if (EMPTYSTR(file))
60621d03121Slukem return(0);
60721d03121Slukem if ((f = fopen(file, "r")) == NULL)
60821d03121Slukem return (0);
609eac5778eSsommerfeld reply(-code, "%s", "");
61021d03121Slukem
61121d03121Slukem for (;
61221d03121Slukem (buf = fparseln(f, &len, NULL, "\0\0\0", 0)) != NULL; free(buf)) {
61321d03121Slukem if (len > 0)
61421d03121Slukem if (buf[len - 1] == '\n')
61521d03121Slukem buf[--len] = '\0';
61673f082e2Slukem cprintf(stdout, " ");
61721d03121Slukem
61821d03121Slukem for (p = buf; *p; p++) {
61921d03121Slukem if (*p == '%') {
62021d03121Slukem p++;
62121d03121Slukem switch (*p) {
62216e88612Slukem
62316e88612Slukem case 'c':
62473f082e2Slukem cprintf(stdout, "%s",
62516e88612Slukem curclass.classname ?
62616e88612Slukem curclass.classname : "<unknown>");
62716e88612Slukem break;
62816e88612Slukem
62921d03121Slukem case 'C':
6303a491edaSlukem if (getcwd(curwd, sizeof(curwd)-1)
6313a491edaSlukem == NULL){
63221d03121Slukem syslog(LOG_WARNING,
63321d03121Slukem "can't getcwd: %s",
63421d03121Slukem strerror(errno));
63521d03121Slukem continue;
63621d03121Slukem }
6373a491edaSlukem cprintf(stdout, "%s", curwd);
63821d03121Slukem break;
63916e88612Slukem
64021d03121Slukem case 'E':
641999fd3d6Slukem if (! EMPTYSTR(emailaddr))
642999fd3d6Slukem cprintf(stdout, "%s",
643999fd3d6Slukem emailaddr);
64421d03121Slukem break;
64516e88612Slukem
64621d03121Slukem case 'L':
64773f082e2Slukem cprintf(stdout, "%s", hostname);
64821d03121Slukem break;
64916e88612Slukem
65016e88612Slukem case 'M':
651999fd3d6Slukem if (curclass.limit == -1) {
65273f082e2Slukem cprintf(stdout, "unlimited");
653999fd3d6Slukem lastnum = 0;
654999fd3d6Slukem } else {
655ee2d1afbSlukem cprintf(stdout, LLF,
656ee2d1afbSlukem (LLT)curclass.limit);
657999fd3d6Slukem lastnum = curclass.limit;
658999fd3d6Slukem }
65916e88612Slukem break;
66016e88612Slukem
66116e88612Slukem case 'N':
662999fd3d6Slukem cprintf(stdout, "%d", connections);
663999fd3d6Slukem lastnum = connections;
66416e88612Slukem break;
66516e88612Slukem
66621d03121Slukem case 'R':
66773f082e2Slukem cprintf(stdout, "%s", remotehost);
66821d03121Slukem break;
66916e88612Slukem
670999fd3d6Slukem case 's':
671999fd3d6Slukem if (lastnum != 1)
672999fd3d6Slukem cprintf(stdout, "s");
673999fd3d6Slukem break;
674999fd3d6Slukem
675999fd3d6Slukem case 'S':
676999fd3d6Slukem if (lastnum != 1)
677999fd3d6Slukem cprintf(stdout, "S");
678999fd3d6Slukem break;
679999fd3d6Slukem
68021d03121Slukem case 'T':
68121d03121Slukem now = time(NULL);
68273f082e2Slukem cprintf(stdout, "%.24s", ctime(&now));
68321d03121Slukem break;
68416e88612Slukem
68521d03121Slukem case 'U':
68673f082e2Slukem cprintf(stdout, "%s",
68721d03121Slukem pw ? pw->pw_name : "<unknown>");
68821d03121Slukem break;
68916e88612Slukem
69021d03121Slukem case '%':
69173f082e2Slukem CPUTC('%', stdout);
69221d03121Slukem break;
69316e88612Slukem
69421d03121Slukem }
69573f082e2Slukem } else
69673f082e2Slukem CPUTC(*p, stdout);
69721d03121Slukem }
69873f082e2Slukem cprintf(stdout, "\r\n");
69921d03121Slukem }
70021d03121Slukem
70121d03121Slukem (void)fflush(stdout);
70221d03121Slukem (void)fclose(f);
70321d03121Slukem return (1);
70421d03121Slukem }
70521d03121Slukem
70631547ec6Slukem /*
707e3a2c5ccSlukem * Parse src, expanding '%' escapes, into dst (which must be at least
708e3a2c5ccSlukem * MAXPATHLEN long).
709e3a2c5ccSlukem */
710e3a2c5ccSlukem void
format_path(char * dst,const char * src)711e3a2c5ccSlukem format_path(char *dst, const char *src)
712e3a2c5ccSlukem {
713e3a2c5ccSlukem size_t len;
714e3a2c5ccSlukem const char *p;
715e3a2c5ccSlukem
716e3a2c5ccSlukem dst[0] = '\0';
717e3a2c5ccSlukem len = 0;
718e3a2c5ccSlukem if (src == NULL)
719e3a2c5ccSlukem return;
720e3a2c5ccSlukem for (p = src; *p && len < MAXPATHLEN; p++) {
721e3a2c5ccSlukem if (*p == '%') {
722e3a2c5ccSlukem p++;
723e3a2c5ccSlukem switch (*p) {
724e3a2c5ccSlukem
725e3a2c5ccSlukem case 'c':
726e3a2c5ccSlukem len += strlcpy(dst + len, curclass.classname,
727e3a2c5ccSlukem MAXPATHLEN - len);
728e3a2c5ccSlukem break;
729e3a2c5ccSlukem
730e3a2c5ccSlukem case 'd':
731e3a2c5ccSlukem len += strlcpy(dst + len, pw->pw_dir,
732e3a2c5ccSlukem MAXPATHLEN - len);
733e3a2c5ccSlukem break;
734e3a2c5ccSlukem
735e3a2c5ccSlukem case 'u':
736e3a2c5ccSlukem len += strlcpy(dst + len, pw->pw_name,
737e3a2c5ccSlukem MAXPATHLEN - len);
738e3a2c5ccSlukem break;
739e3a2c5ccSlukem
740e3a2c5ccSlukem case '%':
741e3a2c5ccSlukem dst[len++] = '%';
742e3a2c5ccSlukem break;
743e3a2c5ccSlukem
744e3a2c5ccSlukem }
745e3a2c5ccSlukem } else
746e3a2c5ccSlukem dst[len++] = *p;
747e3a2c5ccSlukem }
748e3a2c5ccSlukem if (len < MAXPATHLEN)
749e3a2c5ccSlukem dst[len] = '\0';
750e3a2c5ccSlukem dst[MAXPATHLEN - 1] = '\0';
751e3a2c5ccSlukem }
752e3a2c5ccSlukem
753e3a2c5ccSlukem /*
754397e2cfcSlukem * Find s2 at the end of s1. If found, return a string up to (but
75531547ec6Slukem * not including) s2, otherwise returns NULL.
75631547ec6Slukem */
75731547ec6Slukem static char *
strend(const char * s1,char * s2)758dcc88422Slukem strend(const char *s1, char *s2)
75931547ec6Slukem {
76016e88612Slukem static char buf[MAXPATHLEN];
76131547ec6Slukem
76231547ec6Slukem char *start;
76331547ec6Slukem size_t l1, l2;
76431547ec6Slukem
76531547ec6Slukem l1 = strlen(s1);
76631547ec6Slukem l2 = strlen(s2);
76731547ec6Slukem
76853c91d8fSlukem if (l2 >= l1 || l1 >= sizeof(buf))
76931547ec6Slukem return(NULL);
77031547ec6Slukem
77121d03121Slukem strlcpy(buf, s1, sizeof(buf));
77231547ec6Slukem start = buf + (l1 - l2);
77331547ec6Slukem
77431547ec6Slukem if (strcmp(start, s2) == 0) {
77531547ec6Slukem *start = '\0';
77631547ec6Slukem return(buf);
77731547ec6Slukem } else
77831547ec6Slukem return(NULL);
77931547ec6Slukem }
78031547ec6Slukem
78131547ec6Slukem static int
filetypematch(char * types,int mode)782dcc88422Slukem filetypematch(char *types, int mode)
78331547ec6Slukem {
78431547ec6Slukem for ( ; types[0] != '\0'; types++)
78531547ec6Slukem switch (*types) {
78631547ec6Slukem case 'd':
78731547ec6Slukem if (S_ISDIR(mode))
78831547ec6Slukem return(1);
78931547ec6Slukem break;
79031547ec6Slukem case 'f':
79131547ec6Slukem if (S_ISREG(mode))
79231547ec6Slukem return(1);
79331547ec6Slukem break;
79431547ec6Slukem }
79531547ec6Slukem return(0);
79631547ec6Slukem }
79731547ec6Slukem
79831547ec6Slukem /*
79931547ec6Slukem * Look for a conversion. If we succeed, return a pointer to the
80031547ec6Slukem * command to execute for the conversion.
80131547ec6Slukem *
80231547ec6Slukem * The command is stored in a static array so there's no memory
80331547ec6Slukem * leak problems, and not too much to change in ftpd.c. This
80431547ec6Slukem * routine doesn't need to be re-entrant unless we start using a
80531547ec6Slukem * multi-threaded ftpd, and that's not likely for a while...
80631547ec6Slukem */
8070053962fSlukem const char **
do_conversion(const char * fname)808dcc88422Slukem do_conversion(const char *fname)
80931547ec6Slukem {
81031547ec6Slukem struct ftpconv *cp;
81131547ec6Slukem struct stat st;
81231547ec6Slukem int o_errno;
813c22375d4Schristos char *base = NULL;
8140053962fSlukem char *cmd, *p, *lp;
81541592955Schristos char **argv;
816397e2cfcSlukem StringList *sl;
81731547ec6Slukem
81831547ec6Slukem o_errno = errno;
819397e2cfcSlukem sl = NULL;
820397e2cfcSlukem cmd = NULL;
82131547ec6Slukem for (cp = curclass.conversions; cp != NULL; cp = cp->next) {
8220bba8ce3Slukem if (cp->suffix == NULL) {
8230bba8ce3Slukem syslog(LOG_WARNING,
8240bba8ce3Slukem "cp->suffix==NULL in conv list; SHOULDN'T HAPPEN!");
8250bba8ce3Slukem continue;
8260bba8ce3Slukem }
82731547ec6Slukem if ((base = strend(fname, cp->suffix)) == NULL)
82831547ec6Slukem continue;
8290bba8ce3Slukem if (cp->types == NULL || cp->disable == NULL ||
83031547ec6Slukem cp->command == NULL)
83131547ec6Slukem continue;
83231547ec6Slukem /* Is it enabled? */
83331547ec6Slukem if (strcmp(cp->disable, ".") != 0 &&
83431547ec6Slukem stat(cp->disable, &st) == 0)
83531547ec6Slukem continue;
83631547ec6Slukem /* Does the base exist? */
83731547ec6Slukem if (stat(base, &st) < 0)
83831547ec6Slukem continue;
83931547ec6Slukem /* Is the file type ok */
84031547ec6Slukem if (!filetypematch(cp->types, st.st_mode))
84131547ec6Slukem continue;
84231547ec6Slukem break; /* "We have a winner!" */
84331547ec6Slukem }
84431547ec6Slukem
84531547ec6Slukem /* If we got through the list, no conversion */
846397e2cfcSlukem if (cp == NULL)
847397e2cfcSlukem goto cleanup_do_conv;
848397e2cfcSlukem
849397e2cfcSlukem /* Split up command into an argv */
850397e2cfcSlukem if ((sl = sl_init()) == NULL)
851397e2cfcSlukem goto cleanup_do_conv;
8521edbda9aSchristos cmd = ftpd_strdup(cp->command);
853397e2cfcSlukem p = cmd;
854397e2cfcSlukem while (p) {
855397e2cfcSlukem NEXTWORD(p, lp);
856397e2cfcSlukem if (strcmp(lp, "%s") == 0)
857397e2cfcSlukem lp = base;
8581edbda9aSchristos if (sl_add(sl, ftpd_strdup(lp)) == -1)
859397e2cfcSlukem goto cleanup_do_conv;
86031547ec6Slukem }
86131547ec6Slukem
862397e2cfcSlukem if (sl_add(sl, NULL) == -1)
863397e2cfcSlukem goto cleanup_do_conv;
86441592955Schristos argv = sl->sl_str;
865397e2cfcSlukem free(cmd);
866397e2cfcSlukem free(sl);
86741592955Schristos return (void *)(intptr_t)argv;
868397e2cfcSlukem
869397e2cfcSlukem cleanup_do_conv:
870397e2cfcSlukem if (sl)
871397e2cfcSlukem sl_free(sl, 1);
872397e2cfcSlukem free(cmd);
873397e2cfcSlukem errno = o_errno;
874397e2cfcSlukem return(NULL);
87531547ec6Slukem }
87621d03121Slukem
87721d03121Slukem /*
8787e803788Slukem * Count the number of current connections, reading from
8797e803788Slukem * /var/run/ftpd.pids-<class>
8807e803788Slukem * Does a kill -0 on each pid in that file, and only counts
8817e803788Slukem * processes that exist (or frees the slot if it doesn't).
8827e803788Slukem * Adds getpid() to the first free slot. Truncates the file
8837e803788Slukem * if possible.
8847e803788Slukem */
88516e88612Slukem void
count_users(void)886dcc88422Slukem count_users(void)
88716e88612Slukem {
88816e88612Slukem char fn[MAXPATHLEN];
8890053962fSlukem int fd;
8900053962fSlukem size_t i, last, count;
8910053962fSlukem ssize_t scount;
89216e88612Slukem pid_t *pids, mypid;
89316e88612Slukem struct stat sb;
894e9810351Slukem struct flock fl;
89516e88612Slukem
89616e88612Slukem (void)strlcpy(fn, _PATH_CLASSPIDS, sizeof(fn));
89716e88612Slukem (void)strlcat(fn, curclass.classname, sizeof(fn));
89816e88612Slukem pids = NULL;
89916e88612Slukem connections = 1;
900e9810351Slukem fl.l_start = 0;
901e9810351Slukem fl.l_len = 0;
902e9810351Slukem fl.l_pid = 0;
903e9810351Slukem fl.l_type = F_WRLCK;
904e9810351Slukem fl.l_whence = SEEK_SET;
90516e88612Slukem
906f62aa6c8Slukem if ((fd = open(fn, O_RDWR | O_CREAT, 0600)) == -1)
90716e88612Slukem return;
908e9810351Slukem if (fcntl(fd, F_SETLK, &fl) == -1)
909f62aa6c8Slukem goto cleanup_count;
91016e88612Slukem if (fstat(fd, &sb) == -1)
91116e88612Slukem goto cleanup_count;
912*cb93dc63Sshm if ((pids = calloc(sb.st_size + sizeof(pid_t), 1)) == NULL)
91316e88612Slukem goto cleanup_count;
9140053962fSlukem /* XXX: implement a better read loop */
9150053962fSlukem scount = read(fd, pids, sb.st_size);
9160053962fSlukem if (scount == -1 || scount != sb.st_size || scount < 0)
91716e88612Slukem goto cleanup_count;
9180053962fSlukem count = (size_t)scount / sizeof(pid_t);
91916e88612Slukem mypid = getpid();
92016e88612Slukem last = 0;
92116e88612Slukem for (i = 0; i < count; i++) {
92216e88612Slukem if (pids[i] == 0)
92316e88612Slukem continue;
92416e88612Slukem if (kill(pids[i], 0) == -1 && errno != EPERM) {
92516e88612Slukem if (mypid != 0) {
92616e88612Slukem pids[i] = mypid;
92716e88612Slukem mypid = 0;
92816e88612Slukem last = i;
92916e88612Slukem }
93016e88612Slukem } else {
93116e88612Slukem connections++;
93216e88612Slukem last = i;
93316e88612Slukem }
93416e88612Slukem }
93516e88612Slukem if (mypid != 0) {
93616e88612Slukem if (pids[last] != 0)
93716e88612Slukem last++;
93816e88612Slukem pids[last] = mypid;
93916e88612Slukem }
94016e88612Slukem count = (last + 1) * sizeof(pid_t);
94116e88612Slukem if (lseek(fd, 0, SEEK_SET) == -1)
94216e88612Slukem goto cleanup_count;
9430053962fSlukem /* XXX: implement a better write loop */
9440053962fSlukem scount = write(fd, pids, count);
9450053962fSlukem if (scount == -1 || (size_t)scount != count)
94616e88612Slukem goto cleanup_count;
94716e88612Slukem (void)ftruncate(fd, count);
94816e88612Slukem
94916e88612Slukem cleanup_count:
950e9810351Slukem fl.l_type = F_UNLCK;
951e9810351Slukem (void)fcntl(fd, F_SETLK, &fl);
95216e88612Slukem close(fd);
95316e88612Slukem REASSIGN(pids, NULL);
95416e88612Slukem }
955