/* * acpid.c - ACPI daemon * * Portions Copyright (C) 2000 Andrew Henroid * Portions Copyright (C) 2001 Sun Microsystems * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include "acpid.h" #include "log.h" #include "event.h" #include "connection_list.h" #include "proc.h" #include "sock.h" #include "input_layer.h" #include "inotify_handler.h" #include "netlink.h" #include "libc_compat.h" static int handle_cmdline(int *argc, char ***argv); static void close_fds(void); static int daemonize(void); static void open_log(void); static int std2null(void); static int create_pidfile(void); static void clean_exit(int sig); static void reload_conf(int sig); /* do we log event info? */ int logevents; const char *progname; static const char *confdir = ACPID_CONFDIR; static const char *lockfile = ACPID_LOCKFILE; static int nosocket; static int foreground; static const char *pidfile = ACPID_PIDFILE; static int netlink; const char *dropaction = DROP_ACTION; int tpmutefix = 0; int main(int argc, char **argv) { /* learn who we really are */ progname = (const char *)strrchr(argv[0], '/'); progname = progname ? (progname + 1) : argv[0]; /* handle the commandline */ handle_cmdline(&argc, &argv); /* close any extra file descriptors */ close_fds(); /* open the log */ if (foreground) log_to_stderr = 1; else open_log(); /* if we're running in the background, and we're not being started */ /* by systemd */ if (!foreground && !is_socket(STDIN_FILENO)) { if (daemonize() < 0) exit(EXIT_FAILURE); } if (!netlink) { /* open the acpi event file in the proc fs */ /* if the open fails, try netlink */ if (open_proc()) netlink = 1; } if (netlink) { /* open the input layer */ open_input(); /* watch for new input layer devices */ open_inotify(); /* open netlink */ open_netlink(); } /* open our socket */ if (!nosocket) { open_sock(); } /* redirect standard files to /dev/null */ if (std2null() < 0) { exit(EXIT_FAILURE); } acpid_log(LOG_INFO, "starting up with %s", netlink ? "netlink and the input layer" : "proc fs"); /* trap key signals */ signal(SIGHUP, reload_conf); signal(SIGINT, clean_exit); signal(SIGQUIT, clean_exit); signal(SIGTERM, clean_exit); signal(SIGPIPE, SIG_IGN); /* read in our configuration */ if (acpid_read_conf(confdir)) { exit(EXIT_FAILURE); } /* create our pidfile */ if (!foreground && create_pidfile() < 0) { exit(EXIT_FAILURE); } acpid_log(LOG_INFO, "waiting for events: event logging is %s", logevents ? "on" : "off"); /* main loop */ while (1) { fd_set readfds; int nready; int i; struct connection *p; /* it's going to get clobbered, so use a copy */ readfds = *get_fdset(); /* wait on data */ nready = TEMP_FAILURE_RETRY(select(get_highestfd() + 1, &readfds, NULL, NULL, NULL)); if (nready < 0) { acpid_log(LOG_ERR, "select(): %s", strerror(errno)); continue; } /* house keeping */ acpid_close_dead_clients(); /* for each connection */ for (i = 0; i <= get_number_of_connections(); ++i) { int fd; p = get_connection(i); /* if this connection is invalid, bail */ if (!p) break; /* get the file descriptor */ fd = p->fd; /* if this file descriptor has data waiting */ if (FD_ISSET(fd, &readfds)) { /* delegate to this connection's process function */ p->process(fd); } } } clean_exit_with_status(EXIT_SUCCESS); return 0; } /* * Parse command line arguments */ static int handle_cmdline(int *argc, char ***argv) { struct option opts[] = { {"confdir", 1, 0, 'c'}, {"clientmax", 1, 0, 'C'}, {"debug", 0, 0, 'd'}, {"eventfile", 1, 0, 'e'}, {"foreground", 0, 0, 'f'}, {"logevents", 0, 0, 'l'}, {"socketgroup", 1, 0, 'g'}, {"socketmode", 1, 0, 'm'}, {"socketfile", 1, 0, 's'}, {"nosocket", 1, 0, 'S'}, {"pidfile", 1, 0, 'p'}, {"lockfile", 1, 0, 'L'}, {"netlink", 0, 0, 'n'}, {"dropaction", 1, 0, 'r'}, {"tpmutefix", 0, 0, 't'}, {"version", 0, 0, 'v'}, {"help", 0, 0, 'h'}, {NULL, 0, 0, 0}, }; const char *opts_help[] = { "Set the configuration directory.", /* confdir */ "Set the limit on non-root socket connections.",/* clientmax */ "Increase debugging level.",/* debug */ "Use the specified file for events.", /* eventfile */ "Run in the foreground.", /* foreground */ "Log all event activity.", /* logevents */ "Set the group on the socket file.", /* socketgroup */ "Set the permissions on the socket file.",/* socketmode */ "Use the specified socket file.", /* socketfile */ "Do not listen on a UNIX socket (overrides -s).",/* nosocket */ "Use the specified PID file.", /* pidfile */ "Use the specified lockfile to stop processing.", /* lockfile */ "Force netlink/input layer mode. (overrides -e)", /* netlink */ "Define the pseudo-action to drop an event.", /* dropaction */ "Fixup for ThinkPad mute-repeat behaviour.", /* tpmutefix */ "Print version information.", /* version */ "Print this message.", /* help */ }; struct option *opt; const char **hlp; int max, size; for (;;) { int i; i = getopt_long(*argc, *argv, "c:C:de:flg:m:s:Sp:L:nr:tvh", opts, NULL); if (i == -1) { break; } switch (i) { case 'c': confdir = optarg; break; case 'C': clientmax = strtol(optarg, NULL, 0); break; case 'd': debug_level++; break; case 'e': eventfile = optarg; break; case 'f': foreground = 1; break; case 'l': logevents = 1; break; case 'g': socketgroup = optarg; break; case 'm': socketmode = strtol(optarg, NULL, 8); break; case 's': socketfile = optarg; break; case 'S': nosocket = 1; break; case 'p': pidfile = optarg; break; case 'L': lockfile = optarg; break; case 'n': netlink = 1; break; case 'r': dropaction = optarg; break; case 't': tpmutefix = 1; break; case 'v': printf(PACKAGE "-" VERSION "\n"); exit(EXIT_SUCCESS); case 'h': default: fprintf(stderr, "Usage: %s [OPTIONS]\n", progname); max = 0; for (opt = opts; opt->name; opt++) { size = strlen(opt->name); if (size > max) max = size; } for (opt = opts, hlp = opts_help; opt->name; opt++, hlp++) { fprintf(stderr, " -%c, --%s", opt->val, opt->name); size = strlen(opt->name); for (; size < max; size++) fprintf(stderr, " "); fprintf(stderr, " %s\n", *hlp); } exit(EXIT_FAILURE); break; } } *argc -= optind; *argv += optind; return 0; } static void close_fds(void) { struct dirent *dent; DIR *dirp; char *endp; long fd; if ((dirp = opendir("/proc/self/fd")) != NULL) { while ((dent = readdir(dirp)) != NULL) { fd = strtol(dent->d_name, &endp, 10); if (dent->d_name != endp && *endp == '\0' && fd >= 3 && fd != dirfd(dirp)) { close((int) fd); } } closedir(dirp); } } static int daemonize(void) { pid_t pid, sid; /* fork off the parent process */ pid = fork(); if (pid < 0) { acpid_log(LOG_ERR, "fork: %s", strerror(errno)); return -1; } /* if we got a good PID, then we can exit the parent process */ if (pid > 0) { exit(EXIT_SUCCESS); } /* at this point we are executing as the child process */ /* change the umask to something predictable instead of inheriting */ /* whatever from the parent */ umask(0); /* create a new SID for the child process and */ /* detach the process from the parent (normally a shell) */ sid = setsid(); if (sid < 0) { acpid_log(LOG_ERR, "setsid: %s", strerror(errno)); return -1; } /* Change the current working directory. This prevents the current directory from being locked; hence not being able to remove it. */ if (chdir("/") < 0) { acpid_log(LOG_ERR, "chdir(\"/\"): %s", strerror(errno)); return -1; } return 0; } static void open_log(void) { int log_opts; /* open the syslog */ log_opts = LOG_CONS|LOG_NDELAY; openlog(PACKAGE, log_opts, LOG_DAEMON); } static int std2null(void) { int nullfd; /* open /dev/null */ nullfd = open("/dev/null", O_RDWR); if (nullfd < 0) { acpid_log(LOG_ERR, "can't open /dev/null: %s", strerror(errno)); return -1; } /* set up stdin, stdout, stderr to /dev/null */ /* don't redirect stdin if we're being sent a socket by systemd */ if (!is_socket(STDIN_FILENO) && dup2(nullfd, STDIN_FILENO) != STDIN_FILENO) { acpid_log(LOG_ERR, "dup2() stdin: %s", strerror(errno)); close(nullfd); return -1; } if (!log_to_stderr && dup2(nullfd, STDOUT_FILENO) != STDOUT_FILENO) { acpid_log(LOG_ERR, "dup2() stdout: %s", strerror(errno)); close(nullfd); return -1; } if (!log_to_stderr && dup2(nullfd, STDERR_FILENO) != STDERR_FILENO) { acpid_log(LOG_ERR, "dup2() stderr: %s", strerror(errno)); close(nullfd); return -1; } close(nullfd); return 0; } static int create_pidfile(void) { int fd; /* JIC */ unlink(pidfile); /* open the pidfile */ fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644); if (fd >= 0) { FILE *f; /* write our pid to it */ f = fdopen(fd, "w"); if (f != NULL) { fprintf(f, "%d\n", getpid()); fclose(f); /* leave the fd open */ return 0; } close(fd); } /* something went wrong */ acpid_log(LOG_ERR, "can't create pidfile %s: %s", pidfile, strerror(errno)); return -1; } void clean_exit_with_status(int status) { acpid_cleanup_rules(1); delete_all_connections(); acpid_log(LOG_NOTICE, "exiting"); unlink(pidfile); exit(status); } static void clean_exit(int sig __attribute__((unused))) { clean_exit_with_status(EXIT_SUCCESS); } static void reload_conf(int sig __attribute__((unused))) { acpid_log(LOG_NOTICE, "reloading configuration"); acpid_cleanup_rules(0); acpid_read_conf(confdir); } int locked() { struct stat trash; /* check for existence of a lockfile */ return (stat(lockfile, &trash) == 0); }