source: branches/ithildin-1.1/source/main.c @ 832

Revision 832, 22.8 KB checked in by wd, 3 years ago (diff)

Some dmalloc tweaks, fix a dumb server command bug.

  • Property svn:keywords set to Id Rev
Line 
1/*
2 * main.c: the main() function and friends
3 *
4 * Copyright 2002 the Ithildin Project.
5 * See the COPYING file for more information on licensing and use.
6 *
7 * "In My Egotistical Opinion, most people's C programs should be indented
8 * six feet downward and covered with dirt."  -- Blair P. Houghton
9 */
10
11#include <ithildin/stand.h>
12#ifdef HAVE_OPENSSL
13#include <openssl/err.h>
14#include <openssl/rand.h>
15#include <openssl/ssl.h>
16#endif
17#ifdef HAVE_SYS_RESOURCE_H
18#include <sys/resource.h>
19#endif
20
21IDSTRING(rcsid, "$Id$");
22
23/* depending on the platform we might need to declare _malloc_options.  We
24 * don't need to do this for FreeBSD since it's declared in stdlib.h for us
25 * (handy) but we do for any other systems (I guess?) */
26#ifndef __FreeBSD__
27# ifdef USE_INTERNAL_MALLOC
28extern
29# endif
30const char *_malloc_options;
31#endif
32
33/*
34 * the writhing internal mass of the main() function is here, as well as a
35 * few other small things.
36 */
37
38struct me_t me;
39
40static void parse_args(int argc, char **argv);
41HOOK_FUNCTION(stdout_log);
42void sighandler_generic(int sig);
43void sighandler_term(int sig);
44#define SIG_ADD_HANDLER(func, signal) do {                                \
45    struct sigaction sa;                                                  \
46    sa.sa_handler = func;                                                 \
47    sa.sa_flags = 0;                                                      \
48    sigemptyset(&sa.sa_mask);                                             \
49    sigaddset(&sa.sa_mask, signal);                                       \
50    sigaction(signal, &sa, NULL);                                         \
51} while(0);
52
53#ifdef HAVE_OPENSSL
54static void init_ssl(void);
55#endif
56#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
57static void change_rlimit(const char *, int, rlim_t);
58#endif
59
60static bool change_privileges(void);
61
62int main(int argc, char **argv) {
63    char currdir[PATH_MAX];
64    char *s;
65    time_t next;
66       
67#ifdef DEBUG_CODE
68    _malloc_options = "ASJ";
69#endif
70
71    /* initialize ourselves, fill out the 'me' structure as well as possbile */
72    memset(&me, 0, sizeof(struct me_t));
73
74    /* Look at argv[0].  For the purposes of the program we want only the
75     * program's name.  If argv[0] contains a full path name, we truncate it.
76     * If it begins with a - we skip that. */
77    if ((me.execname = strrchr(argv[0], '/')) == NULL)
78        me.execname = argv[0];
79    else
80        me.execname += 1; /* skip the '/' */
81    if (*me.execname == '-')
82        me.execname = me.execname + 1;
83
84    me.started = me.now = time(NULL);
85    strncpy(me.conf_path, CONF_PATH, PATH_MAX);
86    strncpy(me.lib_path, LIB_PATH, PATH_MAX);
87    strncpy(me.data_path, DATA_PATH, PATH_MAX);
88    sprintf(me.conf_file, "%s.conf", me.execname);
89    sprintf(me.version, "%s-%i.%i.%i", BASENAME_VER, MAJOR_VER, MINOR_VER,
90            PATCH_VER);
91    me.revision = REPOVER;
92    me.fork = 1; /* by default we fork */
93
94    /* initialize the hook system before we setup our hooks */
95    init_hooksystem();
96
97    /* set up our static events */
98    me.events.log_debug = create_event(EVENT_FL_NORETURN);
99    me.events.log_notice = create_event(EVENT_FL_NORETURN);
100    me.events.log_warn = create_event(EVENT_FL_NORETURN);
101    me.events.log_error = create_event(EVENT_FL_NORETURN);
102    me.events.log_unknown = create_event(EVENT_FL_NORETURN);
103    me.events.read_conf = create_event(EVENT_FL_NORETURN);
104    me.events.load_module = create_event(EVENT_FL_NORETURN);
105    me.events.unload_module = create_event(EVENT_FL_NORETURN);
106    me.events.afterpoll = create_event(EVENT_FL_NORETURN);
107    me.events.shutdown = create_event(EVENT_FL_NORETURN);
108    me.events.sighup = create_event(EVENT_FL_NORETURN);
109    me.events.sigint = create_event(EVENT_FL_NORETURN);
110    me.events.sigterm = create_event(EVENT_FL_NORETURN);
111    me.events.sigusr1 = create_event(EVENT_FL_NORETURN);
112    me.events.sigusr2 = create_event(EVENT_FL_NORETURN);
113
114    /* immediately register the reload_conf function with sighup */
115    add_hook(me.events.sighup, reload_conf);
116       
117    /* parse our command line arguments */
118    parse_args(argc, argv);
119
120    /* if the user has chosen to fork, install hook events for log output
121     * anyways (but they will be removed when forking occurs */
122    add_hook(me.events.log_notice, stdout_log);
123    add_hook(me.events.log_warn, stdout_log);
124    add_hook(me.events.log_error, stdout_log);
125    add_hook(me.events.log_unknown, stdout_log);
126    if (me.debug)
127        add_hook(me.events.log_debug, stdout_log);
128
129    if (!strcmp(me.execname, "chkconf")) {
130        if (me.debug == false)
131            add_hook(me.events.log_debug, stdout_log);
132        me.debug = true;
133        log_notice("checking configuration file %s/%s", me.conf_path,
134                me.conf_file);
135    } else
136        log_notice("starting %s ...", me.version);
137
138    /* now read in our conf file ... */
139    log_debug("reading conf file from %s...", me.conf_file);
140    getcwd(currdir, PATH_MAX);
141    if (chdir(me.conf_path)) {
142        log_error("couldn't chdir to conf directory %s", me.conf_path);
143            return 1;
144    }
145    me.confhead = read_conf(me.conf_file);
146    if (me.confhead == NULL) {
147        log_error("couldn't parse configuration file %s/%s", me.conf_path,
148                me.conf_file);
149        return 1;
150    }
151    chdir(currdir);
152    if ((s = conf_find_entry("directory", me.confhead, 1)) != NULL) {
153        if (chdir(s) != 0) {
154            log_error("couldn't change to directory %s: %s", s,
155                    strerror(errno));
156            return 1;
157        }
158    }
159    if (!strcmp(me.execname, "chkconf")) {
160        if (me.debug > 1)
161            conf_display_tree(0, me.confhead);
162        log_notice("configuration file %s/%s and includes are okay!",
163                me.conf_path, me.conf_file);
164        exit(0);
165    }
166
167#ifdef HAVE_OPENSSL
168    init_ssl();
169    log_notice("SSL support is %s", (me.ssl.enabled ? "enabled" : "disabled"));
170#endif
171
172#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
173    /* unlimit ourselves in useful fashion */
174# ifdef RLIMIT_CORE
175    change_rlimit("core file size", RLIMIT_CORE, RLIM_INFINITY);
176# endif
177# ifdef RLIMIT_NOFILE
178    change_rlimit("open file descriptors", RLIMIT_NOFILE, 0);
179# endif
180#endif
181
182    /* now drop into the background (yippy-skippy) */
183    if (!me.fork) {
184        log_notice("not forking at user request");
185        log_notice("process id is %d", getpid());
186    }
187    else {
188        pid_t pid;
189        if ((pid = fork()) != 0) {
190            if (pid == -1) {
191                log_error("could not fork into background! PROCESS NOT "
192                        "STARTED!\n");
193                return 1;
194            } else {
195                log_notice("forked to pid %d, server finishing bootup now...",
196                        pid);
197                return 0;
198            }
199        } else {
200            /* this has been added to give the debugger time to attach to the
201             * newly spawned process.  if this inconveniences you, one might
202             * ask why you're running -d anyways? */
203            if (me.debug)
204                sleep(30); /* snooze for 30 seconds */
205        }
206    }
207    /* install signal handlers here */
208    SIG_ADD_HANDLER(SIG_IGN, SIGPIPE);
209    SIG_ADD_HANDLER(SIG_IGN, SIGALRM);
210    SIG_ADD_HANDLER(sighandler_generic, SIGHUP);
211    SIG_ADD_HANDLER(sighandler_generic, SIGINT);
212    SIG_ADD_HANDLER(sighandler_generic, SIGUSR1);
213    SIG_ADD_HANDLER(sighandler_generic, SIGUSR2);
214    SIG_ADD_HANDLER(sighandler_term, SIGTERM);
215       
216    /* initialize the socket system (there are control variables in the
217     * config files, which is why we do it here */
218    init_socketsystem();
219    /* build our modules list and do any auto-loading requested */
220    build_module_list();
221    /* now drop privileges */
222    if (!change_privileges())
223        return 1;
224
225    /* trounce those stdout log bits (we do it here and not earlier in case
226     * there is any important output from the socket/module init functions */
227    if (me.fork) {
228        remove_hook(me.events.log_notice, stdout_log);
229        remove_hook(me.events.log_warn, stdout_log);
230        remove_hook(me.events.log_error, stdout_log);
231        remove_hook(me.events.log_unknown, stdout_log);
232        if (me.debug)
233            remove_hook(me.events.log_debug, stdout_log);
234    }
235
236    /* loop until poll_sockets returns 0 */
237    next = 1; /* fuh */
238    while (!me.shutdown && poll_sockets(next)) {
239        reap_dead_sockets();
240        next = exec_timers();
241        hook_event(me.events.afterpoll, NULL);
242        if (me.reloads) {
243            do_module_reloads();
244            me.reloads = 0;
245        }
246    }
247       
248    if (me.shutdown) {
249        hook_event(me.events.shutdown, NULL);
250        /* exit_process will exit for us. */
251        exit_process(NULL, NULL);
252    }
253
254    log_error("going down in flames...");
255    exit(1); /* this is a bad exit...  this should really never happen */
256}
257
258static const char *usage = "\
259usage: %s [-Cdhmv] [-c file] [-l path] [-p path] [file]\n\
260      -c <file>    loads the specified configuration file\n\
261      -C           invoke in configuration checking-mode; the server does\n\
262                   not start, but simply checks the syntax of the config\n\
263                   files (-p and -c also work)\n\
264      -d           turns on debugging (log_debug type messages are logged)\n\
265      -h           displays this help data\n\
266      -l <path>    changes the default library path to the specified one\n\
267      -n           prevents the daemon from forking (and logs to stdout)\n\
268      -p <path>    changes the default configuration path to the specified one\n\
269      -v           displays pertinent version information\n\
270";
271
272void parse_args(int argc, char **argv) {
273    char c;
274
275    while ((c = getopt(argc, argv, "c:Cdhl:np:v")) != -1) {
276        switch (c) {
277            case 'c':
278                if (strrchr(optarg, '/') != NULL)
279                    snprintf(me.conf_file, PATH_MAX, "%s",
280                            strrchr(optarg, '/') + 1);
281                else
282                    strncpy(me.conf_file, optarg, PATH_MAX);
283                break;
284            case 'C':
285                /* dirty hack */
286                me.execname = "chkconf";
287                break;
288            case 'd':
289                me.debug++;
290                break;
291            case 'h':
292                printf(usage, me.execname);
293                exit(0);
294            case 'l':
295                strncpy(me.lib_path, optarg, PATH_MAX);
296                break;
297            case 'n':
298                me.fork = 0;
299                add_hook(me.events.sigint, exit_process);
300                break;
301            case 'p':
302                strncpy(me.conf_path, optarg, PATH_MAX);
303                break;
304            case 'v':
305                printf("%s (r%d)\n", me.version, me.revision);
306                printf("CFLAGS=%s\nCFLAGSDLL=%s\nLDFLAGS=%s\nLDFLAGSDLL=%s\n",
307                        COMP_FLAGS, COMP_FLAGS_MOD, COMP_LDFLAGS,
308                        COMP_LDFLAGS_MOD);
309                printf("CONF_PATH=%s\nLIB_PATH=%s\nDATA_PATH=%s\n", CONF_PATH,
310                        LIB_PATH, DATA_PATH);
311                exit(0);
312            case '?':
313                printf(usage, me.execname);
314                exit(1);
315        }
316    }
317
318    /* see if the conf file is named on the command line */
319    if (argc > optind) {
320        if (strrchr(argv[optind], '/') != NULL)
321            snprintf(me.conf_file, PATH_MAX, "%s",
322                    strrchr(argv[optind], '/') + 1);
323        else
324            strncpy(me.conf_file, argv[optind], PATH_MAX);
325    }
326}
327
328HOOK_FUNCTION(stdout_log) {
329    struct log_event_data *ldp = (struct log_event_data *)data;
330
331    printf("%8s: %s%s%s\n", log_conv_str(ldp->level), ldp->module,
332            (*ldp->module != '\0' ? ": " : ""), ldp->msg);
333    return NULL;
334}
335
336HOOK_FUNCTION(reload_conf) {
337    char currdir[PATH_MAX];
338    conf_list_t *newconf = NULL;
339    conf_list_t *oldconf = NULL;
340
341    getcwd(currdir, PATH_MAX);
342    if (chdir(me.conf_path)) {
343        log_error("couldn't chdir to conf directory %s while reloading conf ",
344                me.conf_path);
345        return NULL;
346    }
347    newconf = read_conf(me.conf_file);
348    if (newconf == NULL) {
349        log_error("couldn't read configuration file %s while reloading conf!",
350                me.conf_file);
351        return NULL;
352    }
353    chdir(currdir);
354
355    oldconf = me.confhead;
356    me.confhead = newconf;
357    build_module_list(); /* rebuild the module list */
358    hook_event(me.events.read_conf, (void *)me.confhead);
359    destroy_conf_branch(oldconf);
360    log_notice("reloaded configuration file %s/%s", me.conf_path,
361            me.conf_file);
362
363    return NULL;
364}
365
366/* eventually aborting existence should consist of more cleanup, right now
367 * all we do is destroy all our sockets.  probably what I should do is write
368 * a *_shutdown function for each subsystem.  later.  */
369HOOK_FUNCTION(exit_process) {
370    struct isocket *sp;
371
372    /* if this function is called pre-shutdown, simply set the shutdown flag,
373     * we will get called again later to do cleanup. */
374    if (me.shutdown == 0) {
375        me.shutdown = -1;
376        return NULL;
377    }
378
379#ifdef USE_DMALLOC
380    dmalloc_log_stats();
381    dmalloc_log_unfreed();
382#endif
383
384    /* unload all our modules */
385    unload_all_modules();
386
387    /* free our conf stuff */
388    destroy_conf_branch(me.confhead);
389
390    /* thrash all our sockets */
391    reap_dead_sockets();
392    LIST_FOREACH(sp, &allsockets, intlp) {
393        destroy_socket(sp);
394    }
395    reap_dead_sockets();
396
397#ifdef HAVE_OPENSSL
398    if (me.ssl.enabled)
399        ERR_free_strings();
400#endif
401
402#ifdef USE_DMALLOC
403    dmalloc_log_stats();
404    dmalloc_log_unfreed();
405#endif
406
407    exit(0);
408    return NULL; /* NOTREACHED */
409}
410
411void sighandler_generic(int sig) {
412    switch (sig) {
413        case SIGHUP:
414            log_notice("signal SIGHUP received");
415            hook_event(me.events.sighup, NULL);
416            break;
417        case SIGINT:
418            log_notice("signal SIGINT received");
419            hook_event(me.events.sigint, NULL);
420            break;
421        case SIGUSR1:
422            log_notice("signal SIGUSR1 received");
423            hook_event(me.events.sigusr1, NULL);
424            break;
425        case SIGUSR2:
426            log_notice("signal SIGUSR2 received");
427            hook_event(me.events.sigusr2, NULL);
428            break;
429        default:
430            log_warn("sighandler_generic called with unknown signal %d", sig);
431            break;
432    }
433    return;
434}
435
436void sighandler_term(int sig) {
437    switch (sig) {
438        case SIGTERM:
439            log_notice("signal SIGTERM received");
440            hook_event(me.events.sigterm, NULL);
441            break;
442        default:
443            log_warn("sighandler_term called with unknown signal %d", sig);
444            break;
445    }
446    me.shutdown = SIGTERM; /* shutting down */
447}
448
449#ifdef HAVE_OPENSSL
450/* This function initializes the non-socket parts of SSL.  If SSL support is
451 * not compiled in, it simply sets me.have_ssl to 0 and returns.  Otherwise it
452 * looks for proper configuration data to enable SSL in the daemon. */
453static void init_ssl(void) {
454    conf_list_t *clp;
455    char *s;
456    size_t ebytes = 128;
457
458    /* First check to see if they configured SSL at all.  Not configuring it is
459     * the equivalent of runtime disabling. */
460    if ((clp = conf_find_list("ssl", me.confhead, 1)) == NULL) {
461        log_notice("no ssl configuration section found.");
462        return;
463    }
464
465    /* Now initialize the SSL library */
466    if (!SSL_library_init()) {
467        log_error("could not initialize OpenSSL library.");
468        exit(1);
469    }
470    SSL_load_error_strings();
471
472    /* Now try to seed the SSL PRNG. */
473    if ((s = conf_find_entry("entropy-bits", clp, 1)) != NULL)
474        ebytes = str_conv_int(s, 1024) / 8;
475    if ((s = conf_find_entry("entropy", clp, 1)) == NULL) {
476        log_warn("no entropy source defined in the ssl section.");
477        return;
478    }
479    if (!strncmp(s, "egd:", 4)) {
480        s += 4;
481        if ((ebytes = RAND_egd_bytes(s, ebytes)) <= 0) {
482            log_error("could not seed the OpenSSL PRNG using EGD socket %s: "
483                    "%s", s, ERR_error_string(ERR_get_error(), NULL));
484            return;
485        } else
486            log_debug("egd socket on %s yielded %d bits of entropy", s,
487                    ebytes * 8);
488    } else {
489        if ((ebytes = RAND_load_file(s, ebytes)) <= 0) {
490            log_error("could not seed the OpenSSL PRNG using random data "
491                    "from %s: %s", s, ERR_error_string(ERR_get_error(), NULL));
492            return;
493        } else
494            log_debug("entropy file %s yielded %d bits of entropy", s,
495                    ebytes * 8);
496    }
497
498    /* And lastly, check to see that we have at least key/cert files.  also
499     * look for an (optional) CA file.  XXX: at present we do not support CA
500     * directories.  If this is desirable support will be added. */
501    if ((s = conf_find_entry("certificate-file", clp, 1)) == NULL) {
502        log_error("certificate file not defined.  ssl will be disabled.");
503        return;
504    }
505    if (*s == '/')
506        strlcpy(me.ssl.certfile, s, PATH_MAX);
507    else
508        snprintf(me.ssl.certfile, PATH_MAX, "%s/%s", me.conf_path, s);
509    if (access(me.ssl.certfile, R_OK) != 0) {
510        log_error("could not access certificate file %s: %s", me.ssl.certfile,
511                strerror(errno));
512        return;
513    }
514
515    if ((s = conf_find_entry("key-file", clp, 1)) == NULL) {
516        log_error("private key file not defined.  ssl will be disabled.");
517        return;
518    }
519    if (*s == '/')
520        strlcpy(me.ssl.keyfile, s, PATH_MAX);
521    else
522        snprintf(me.ssl.keyfile, PATH_MAX, "%s/%s", me.conf_path, s);
523    if (access(me.ssl.keyfile, R_OK) != 0) {
524        log_error("could not access key file %s: %s", me.ssl.keyfile,
525                strerror(errno));
526        return;
527    }
528
529    /* the CA file is optional, but if it defined it must be accessible. */
530    if ((s = conf_find_entry("ca-file", clp, 1)) != NULL) {
531        if (*s == '/')
532            strlcpy(me.ssl.cafile, s, PATH_MAX);
533        else
534            snprintf(me.ssl.cafile, PATH_MAX, "%s/%s", me.conf_path, s);
535        if (access(me.ssl.cafile, R_OK) != 0) {
536            log_error("could not access CA file %s: %s", me.ssl.cafile,
537                    strerror(errno));
538            return;
539        }
540    }
541
542    me.ssl.verify = str_conv_bool(
543            conf_find_entry("verify-certificates", clp, 1), 1);
544    me.ssl.hs_timeout = str_conv_time(
545            conf_find_entry("handshake-timeout", clp, 1), 30);
546
547    me.ssl.enabled = true; /* enable SSL. */
548}
549#endif
550
551#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
552/* rlimit-changer, with cute output. */
553static void change_rlimit(const char *name, int var, rlim_t value) {
554    struct rlimit rl;
555
556    if (getrlimit(var, &rl) == 0) {
557        if (value == 0)
558            value = rl.rlim_max;
559        rl.rlim_cur = value;
560        if (setrlimit(var, &rl) == 0) {
561            if (rl.rlim_cur == RLIM_INFINITY)
562                log_notice("Resource limit for %s set to infinity.", name);
563            else
564                log_notice("Resource limit for %s set to %lld", name,
565                        (int64_t)rl.rlim_cur);
566        } else {
567            if (value == RLIM_INFINITY) {
568                getrlimit(var, &rl); /* in case rl got trashed.. */
569                rl.rlim_cur = rl.rlim_max;
570                if (setrlimit(var, &rl) == 0) {
571                    log_debug("Failed to set resource limit for %s to "
572                            "infinity.", name);
573                    log_notice("Resource limit for %s set to %lld", name,
574                            (int64_t)rl.rlim_cur);
575                    return;
576                }
577            }
578            log_notice("Failed to change resource limit for %s, currently "
579                    "%lld", name, (int64_t)rl.rlim_cur);
580        }
581    }
582}
583#endif
584
585/* This is the functionality for dropping privileges at run-time. */
586#ifdef HAVE_GRP_H
587#include <grp.h>
588#endif
589#ifdef HAVE_PWD_H
590#include <pwd.h>
591#endif
592static bool change_privileges(void) {
593    char *s;
594    bool drop_all = str_conv_bool(conf_find_entry("drop-privileges",
595                me.confhead, 1), true);
596
597#if defined(HAVE_GETGRNAM) && defined(HAVE_GETGID) && defined(HAVE_GETUID) \
598    && defined(HAVE_SETGID)
599# ifndef NGROUPS_MAX
600# define NGROUPS_MAX 32
601# endif
602    if (getuid() == 0 &&
603            (s = conf_find_entry("groups", me.confhead, 1)) != NULL) {
604        struct group *grp;
605        gid_t gids[NGROUPS_MAX];
606        int cnt = 0;
607        char *cur;
608
609        while ((cur = strsep(&s, ",\t ")) != NULL) {
610            if (*cur == '\0')
611                continue;
612            if ((grp = getgrnam(cur)) != NULL) {
613                if (cnt == 0 && !drop_all) {
614                    /* if this is our first group and drop_all is not set ten
615                     * call setegid() */
616# if defined(HAVE_GETEGID) && defined(HAVE_SETEGID)
617                    setegid(grp->gr_gid);
618                    if (getegid() != grp->gr_gid) {
619                        log_error("could not change effective gid to %s (%d)",
620                                gr->gr_name, gr->gid);
621                        return false;
622                    }
623# endif
624                }
625                gids[cnt++] = grp->gr_gid;
626
627# ifndef HAVE_SETGROUPS
628#  ifdef HAVE_SETEGID
629                /* only make this conditional if we could change the egid
630                 * above. */
631                if (drop_all)
632#  endif
633                {
634                    setgid(grp->gr_gid);
635                    if (getgid() != grp->gr_gid) {
636                        log_error("could not change to group %s (%d)",
637                                grp->gr_name, grp->gr_gid);
638                        return false;
639                    }
640                }
641                break;
642# endif
643            } else {
644                log_error("could not find group %s", cur);
645                return false;
646            }
647        }
648
649# ifdef HAVE_SETGROUPS
650        if (setgroups(cnt, gids) != 0) {
651            log_error("could not change group list: %s", strerror(errno));
652            return false;
653        }
654# endif
655    }
656#endif
657
658#if defined(HAVE_GETPWNAM) && defined(HAVE_GETUID)
659    if (getuid() == 0 &&
660            (s = conf_find_entry("username", me.confhead, 1)) != NULL) {
661        struct passwd *pwd = getpwnam(s);
662
663        if (pwd != NULL) {
664            if (drop_all) {
665                if (setuid(pwd->pw_uid) != 0) {
666                    log_error("could not change uid: %s", strerror(errno));
667                    return false;
668                }
669                log_notice("changed effective and real uid to %s (%d)",
670                        pwd->pw_name, pwd->pw_uid);
671            } else {
672                /* only change the effective uid. */
673# if defined(HAVE_GETEUID) && defined(HAVE_SETEUID)
674                if (seteuid(pwd->pw_uid) != 0) {
675                    log_error("could not euid: %s", strerror(errno));
676                    return false;
677                }
678                log_notice("changed effective uid to %s (%d)", pwd->pw_name,
679                        pwd->pw_uid);
680# endif
681            }
682        } else {
683            log_error("could not find user %s", s);
684            return false;
685        }
686    }
687#endif
688
689    return true;
690}
691
692/* vi:set ts=8 sts=4 sw=4 tw=76 et: */
Note: See TracBrowser for help on using the repository browser.