int ud_create_socket(const char *name, mode_t socketmode) { int fd; int r; struct sockaddr_un uds_addr; if (strnlen(name, sizeof(uds_addr.sun_path)) > sizeof(uds_addr.sun_path) - 1) { acpid_log(LOG_ERR, "ud_create_socket(): " "socket filename longer than %zu characters: %s", sizeof(uds_addr.sun_path) - 1, name); errno = EINVAL; return -1; } /* JIC */ unlink(name); fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); if (fd < 0) { return fd; } /* Clear the umask to guarantee predictable results from fchmod(). */ umask(0); if (fchmod(fd, socketmode) < 0) { close(fd); acpid_log(LOG_ERR, "fchmod() on socket %s: %s", name, strerror(errno)); return -1; } /* setup address struct */ memset(&uds_addr, 0, sizeof(uds_addr)); uds_addr.sun_family = AF_UNIX; strncpy(uds_addr.sun_path, name, sizeof(uds_addr.sun_path) - 1); /* bind it to the socket */ r = bind(fd, (struct sockaddr *)&uds_addr, sizeof(uds_addr)); if (r < 0) { close (fd); return r; } /* listen - allow 10 to queue */ r = listen(fd, 10); if (r < 0) { close(fd); return r; } return fd; }
static char * read_line(int fd) { static char *buf; int buflen = 64; int i = 0; int r; int searching = 1; while (searching) { /* ??? This memory is leaked since it is never freed */ buf = realloc(buf, buflen); if (!buf) { acpid_log(LOG_ERR, "malloc(%d): %s", buflen, strerror(errno)); return NULL; } memset(buf+i, 0, buflen-i); while (i < buflen) { r = read(fd, buf+i, 1); if (r < 0 && errno != EINTR) { /* we should do something with the data */ acpid_log(LOG_ERR, "read(): %s", strerror(errno)); return NULL; } else if (r == 0) { /* signal this in an almost standard way */ errno = EPIPE; return NULL; } else if (r == 1) { /* scan for a newline */ if (buf[i] == '\n') { searching = 0; buf[i] = '\0'; break; } i++; } } if (buflen >= MAX_BUFLEN) { break; } buflen *= 2; } return buf; }
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\n", pidfile, strerror(errno)); return -1; }
int ud_connect(const char *name) { int fd; int r; struct sockaddr_un addr; if (strnlen(name, sizeof(addr.sun_path)) > sizeof(addr.sun_path) - 1) { acpid_log(LOG_ERR, "ud_connect(): " "socket filename longer than %zu characters: %s", sizeof(addr.sun_path) - 1, name); errno = EINVAL; return -1; } fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) { return fd; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; sprintf(addr.sun_path, "%s", name); /* safer: */ /*strncpy(addr.sun_path, name, sizeof(addr.sun_path) - 1);*/ r = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); if (r < 0) { close(fd); return r; } return fd; }
static void reload_conf(int sig __attribute__((unused))) { acpid_log(LOG_NOTICE, "reloading configuration\n"); acpid_cleanup_rules(0); acpid_read_conf(confdir); }
static void clean_exit_with_status(int status) { acpid_cleanup_rules(1); acpid_log(LOG_NOTICE, "exiting\n"); unlink(pidfile); exit(status); }
static void process_proc(int fd) { char *event; /* read an event */ event = read_line(fd); /* if we're locked, don't process the event */ if (locked()) { if (logevents && event != NULL) { acpid_log(LOG_INFO, "lockfile present, not processing " "event \"%s\"", event); } return; } /* handle the event */ if (event) { if (logevents) { acpid_log(LOG_INFO, "procfs received event \"%s\"", event); } acpid_handle_event(event); if (logevents) { acpid_log(LOG_INFO, "procfs completed event \"%s\"", event); } } else if (errno == EPIPE) { acpid_log(LOG_WARNING, "events file connection closed"); exit(EXIT_FAILURE); } else { static int nerrs; if (++nerrs >= ACPID_MAX_ERRS) { acpid_log(LOG_ERR, "too many errors reading " "events file - aborting"); exit(EXIT_FAILURE); } } }
/* accept a new client connection */ static void process_sock(int fd) { int cli_fd; struct ucred creds; char *buf; static int accept_errors; /* accept and add to our lists */ cli_fd = ud_accept(fd, &creds); if (cli_fd < 0) { acpid_log(LOG_ERR, "can't accept client: %s", strerror(errno)); accept_errors++; if (accept_errors >= 5) { acpid_log(LOG_ERR, "giving up"); clean_exit_with_status(EXIT_FAILURE); } return; } accept_errors = 0; /* don't allow too many non-root clients */ if (creds.uid != 0 && non_root_clients >= clientmax) { close(cli_fd); acpid_log(LOG_ERR, "too many non-root clients"); return; } if (creds.uid != 0) { non_root_clients++; } if(asprintf(&buf, "%d[%d:%d]", creds.pid, creds.uid, creds.gid) < 0) { close(cli_fd); acpid_log(LOG_ERR, "asprintf: %s", strerror(errno)); return; } acpid_add_client(cli_fd, buf); free(buf); }
/* Set up an inotify watch on /dev/input. */ void open_inotify(void) { int fd = -1; int wd = -1; struct connection c; /* set up inotify */ fd = inotify_init1(IN_CLOEXEC); if (fd < 0) { acpid_log(LOG_ERR, "inotify_init() failed: %s (%d)", strerror(errno), errno); return; } acpid_log(LOG_DEBUG, "inotify fd: %d", fd); /* watch for files being created or deleted in /dev/input */ wd = inotify_add_watch(fd, ACPID_INPUTLAYERDIR, IN_CREATE | IN_DELETE); if (wd < 0) { acpid_log(LOG_ERR, "inotify_add_watch() failed: %s (%d)", strerror(errno), errno); close(fd); return; } acpid_log(LOG_DEBUG, "inotify wd: %d", wd); /* add a connection to the list */ c.fd = fd; c.process = process_inotify; c.pathname = NULL; c.kybd = 0; add_connection(&c); }
int open_proc() { int fd; struct connection c; /* O_CLOEXEC: Make sure scripts we exec() (in event.c) don't get our file descriptors. */ fd = open(eventfile, O_RDONLY | O_CLOEXEC); if (fd < 0) { if (errno == ENOENT) { acpid_log(LOG_DEBUG, "Deprecated %s was not found. " "Trying netlink and the input layer...", eventfile); } else { acpid_log(LOG_ERR, "can't open %s: %s (%d)", eventfile, strerror(errno), errno); } return -1; } acpid_log(LOG_DEBUG, "proc fs opened successfully"); /* add a connection to the list */ c.fd = fd; c.process = process_proc; c.pathname = NULL; c.kybd = 0; if (add_connection(&c) < 0) { close(fd); acpid_log(LOG_ERR, "can't add connection for %s", eventfile); return -1; } return 0; }
static char * read_line(int fd) { static char buf[BUFLEN]; int i = 0; int r; int searching = 1; while (searching) { memset(buf+i, 0, BUFLEN-i); /* only go to BUFLEN-1 so there will always be a 0 at the end */ while (i < BUFLEN-1) { r = TEMP_FAILURE_RETRY(read(fd, buf+i, 1)); if (r < 0) { /* we should do something with the data */ acpid_log(LOG_ERR, "read(): %s", strerror(errno)); return NULL; } else if (r == 0) { /* signal this in an almost standard way */ errno = EPIPE; return NULL; } else if (r == 1) { /* scan for a newline */ if (buf[i] == '\n') { searching = 0; buf[i] = '\0'; break; } i++; } } if (i >= BUFLEN - 1) break; } return buf; }
void add_connection(struct connection *p) { if (nconnections < 0) return; if (nconnections >= MAX_CONNECTIONS) { acpid_log(LOG_ERR, "Too many connections.\n"); /* ??? This routine should return -1 in this situation so that */ /* callers can clean up any open fds and whatnot. */ return; } if (nconnections == 0) FD_ZERO(&allfds); /* add the connection to the connection list */ connection_list[nconnections] = *p; ++nconnections; /* add to the fd set */ FD_SET(p->fd, &allfds); highestfd = max(highestfd, p->fd); }
/* set up the socket for client connections */ void open_sock() { int fd; struct connection c; /* if this is a socket passed in via stdin by systemd */ if (is_socket(STDIN_FILENO)) { fd = STDIN_FILENO; /* ??? Move CLOEXEC and NONBLOCK settings below up to here? */ } else { /* create our own socket */ fd = ud_create_socket(socketfile, socketmode); if (fd < 0) { acpid_log(LOG_ERR, "can't open socket %s: %s", socketfile, strerror(errno)); exit(EXIT_FAILURE); } /* if we need to change the socket's group, do so */ if (socketgroup) { struct group *gr; struct stat buf; gr = getgrnam(socketgroup); if (!gr) { acpid_log(LOG_ERR, "group %s does not exist", socketgroup); exit(EXIT_FAILURE); } if (fstat(fd, &buf) < 0) { acpid_log(LOG_ERR, "can't stat %s: %s", socketfile, strerror(errno)); exit(EXIT_FAILURE); } /* ??? I've tried using fchown(), however it doesn't work here. * It also doesn't work before bind(). The GNU docs seem to * indicate it isn't supposed to work with sockets. */ /* if (fchown(fd, buf.st_uid, gr->gr_gid) < 0) { */ if (chown(socketfile, buf.st_uid, gr->gr_gid) < 0) { acpid_log(LOG_ERR, "can't chown %s: %s", socketfile, strerror(errno)); exit(EXIT_FAILURE); } } } /* Don't leak fds when execing. * ud_create_socket() already does this, but there is no guarantee that * a socket sent in via STDIN will have this set. */ if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { close(fd); acpid_log(LOG_ERR, "fcntl() on socket %s for FD_CLOEXEC: %s", socketfile, strerror(errno)); return; } /* Avoid a potential hang. * ud_create_socket() already does this, but there is no guarantee that * a socket sent in via STDIN will have this set. */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { close(fd); acpid_log(LOG_ERR, "fcntl() on socket %s for O_NONBLOCK: %s", socketfile, strerror(errno)); return; } /* add a connection to the list */ c.fd = fd; c.process = process_sock; c.pathname = NULL; c.kybd = 0; if (add_connection(&c) < 0) { close(fd); acpid_log(LOG_ERR, "can't add connection for socket %s", socketfile); return; } }
static void format_netlink(struct nlmsghdr *msg) { struct rtattr *tb[ACPI_GENL_ATTR_MAX + 1]; struct genlmsghdr *ghdr = NLMSG_DATA(msg); int len; struct rtattr *attrs; len = msg->nlmsg_len; /* if this message doesn't have the proper family ID, drop it */ if (msg->nlmsg_type != acpi_ids_getfamily()) { if (logevents) { acpid_log(LOG_INFO, "wrong netlink family ID.\n"); } return; } len -= NLMSG_LENGTH(GENL_HDRLEN); if (len < 0) { acpid_log(LOG_WARNING, "wrong netlink controller message len: %d\n", len); return; } attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN); /* parse the attributes in this message */ parse_rtattr(tb, ACPI_GENL_ATTR_MAX, attrs, len); /* if there's an ACPI event attribute... */ if (tb[ACPI_GENL_ATTR_EVENT]) { /* get the actual event struct */ struct acpi_genl_event *event = RTA_DATA(tb[ACPI_GENL_ATTR_EVENT]); char buf[64]; /* format it */ snprintf(buf, sizeof(buf), "%s %s %08x %08x", event->device_class, event->bus_id, event->type, event->data); /* if we're locked, don't process the event */ if (locked()) { if (logevents) { acpid_log(LOG_INFO, "lockfile present, not processing " "netlink event \"%s\"\n", buf); } return; } if (logevents) acpid_log(LOG_INFO, "received netlink event \"%s\"\n", buf); /* send the event off to the handler */ acpid_handle_event(buf); if (logevents) acpid_log(LOG_INFO, "completed netlink event \"%s\"\n", buf); } }
/* (based on rtnl_listen() in libnetlink.c) */ void process_netlink(int fd) { int status; struct nlmsghdr *h; /* the address for recvmsg() */ struct sockaddr_nl nladdr; /* the io vector for recvmsg() */ struct iovec iov; /* recvmsg() parameters */ struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; /* buffer for the incoming data */ char buf[8192]; static int nerrs; /* set up the netlink address */ memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; /* set up the I/O vector */ iov.iov_base = buf; iov.iov_len = sizeof(buf); /* read the data into the buffer */ status = recvmsg(fd, &msg, 0); /* if there was a problem, print a message and keep trying */ if (status < 0) { /* if we were interrupted by a signal, bail */ if (errno == EINTR) return; acpid_log(LOG_ERR, "netlink read error: %s (%d)\n", strerror(errno), errno); if (++nerrs >= ACPID_MAX_ERRS) { acpid_log(LOG_ERR, "too many errors reading via " "netlink - aborting\n"); exit(EXIT_FAILURE); } return; } /* if an orderly shutdown has occurred, we're done */ if (status == 0) { acpid_log(LOG_WARNING, "netlink connection closed\n"); exit(EXIT_FAILURE); } /* check to see if the address length has changed */ if (msg.msg_namelen != sizeof(nladdr)) { acpid_log(LOG_WARNING, "netlink unexpected length: " "%d expected: %d\n", msg.msg_namelen, sizeof(nladdr)); return; } /* for each message received */ for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); if (l < 0 || len > status) { if (msg.msg_flags & MSG_TRUNC) { acpid_log(LOG_WARNING, "netlink msg truncated (1)\n"); return; } acpid_log(LOG_WARNING, "malformed netlink msg, length %d\n", len); return; } /* format the message */ format_netlink(h); status -= NLMSG_ALIGN(len); h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { acpid_log(LOG_WARNING, "netlink msg truncated (2)\n"); return; } if (status) { acpid_log(LOG_WARNING, "netlink remnant of size %d\n", status); return; } return; } /* convert the netlink multicast group number into a bit map */ /* (e.g. 4 => 16, 5 => 32) */ static __u32 nl_mgrp(__u32 group) { if (group > 31) { acpid_log(LOG_ERR, "Unexpected group number %d\n", group); return 0; } return group ? (1 << (group - 1)) : 0; } void open_netlink(void) { struct rtnl_handle rth; struct connection c; /* open the appropriate netlink socket for input */ if (rtnl_open_byproto( &rth, nl_mgrp(acpi_ids_getgroup()), NETLINK_GENERIC) < 0) { acpid_log(LOG_ERR, "cannot open generic netlink socket\n"); return; } acpid_log(LOG_DEBUG, "netlink opened successfully\n"); /* add a connection to the list */ c.fd = rth.fd; c.process = process_netlink; add_connection(&c); }
int main(int argc, char **argv) { int event_fd; int sock_fd = -1; /* init to avoid a compiler warning */ /* 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(); /* actually open the event file */ event_fd = open(eventfile, O_RDONLY); if (event_fd < 0) { fprintf(stderr, "%s: can't open %s: %s\n", progname, eventfile, strerror(errno)); exit(EXIT_FAILURE); } fcntl(event_fd, F_SETFD, FD_CLOEXEC); /* * if there is data, and the kernel is NOT broken, this eats 1 byte. We * can't have that. This is ifdef'ed out on the assumption that old kernels * are out of popular use, by now. */ #ifdef TEST_FOR_BAD_KERNELS /* * Older kernels did not support read() properly or poll() at all * Check that the kernel supports the proper semantics, or die. * * Good kernels will respect O_NONBLOCK and return -1. Bad kernels * will ignore O_NONBLOCK and return 0. Really bad kernels will block * and overflow the buffer. Can't deal with the really bad ones. */ { int fl; char buf; fl = fcntl(event_fd, F_GETFL); fcntl(event_fd, F_SETFL, fl | O_NONBLOCK); if (read(event_fd, &buf, 1) == 0) { fprintf(stderr, "%s: this kernel does not support proper " "event file handling.\n" "Please get the patch from " "http://acpid.sourceforge.net\n", progname); exit(EXIT_FAILURE); } fcntl(event_fd, F_SETFL, fl); } #endif /* open our socket */ if (!nosocket) { sock_fd = ud_create_socket(socketfile); if (sock_fd < 0) { fprintf(stderr, "%s: can't open socket %s: %s\n", progname, socketfile, strerror(errno)); exit(EXIT_FAILURE); } fcntl(sock_fd, F_SETFD, FD_CLOEXEC); chmod(socketfile, socketmode); if (socketgroup) { struct group *gr; struct stat buf; gr = getgrnam(socketgroup); if (!gr) { fprintf(stderr, "%s: group %s does not exist\n", progname, socketgroup); exit(EXIT_FAILURE); } if (stat(socketfile, &buf) < 0) { fprintf(stderr, "%s: can't stat %s\n", progname, socketfile); exit(EXIT_FAILURE); } if (chown(socketfile, buf.st_uid, gr->gr_gid) < 0) { fprintf(stderr, "%s: chown(): %s\n", progname, strerror(errno)); exit(EXIT_FAILURE); } } } /* if we're running in foreground, we don't daemonize */ if (!foreground) { if (daemonize() < 0) exit(EXIT_FAILURE); } /* open the log */ if (open_log() < 0) { exit(EXIT_FAILURE); } acpid_log(LOG_INFO, "starting up\n"); /* 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 (create_pidfile() < 0) { exit(EXIT_FAILURE); } /* main loop */ acpid_log(LOG_INFO, "waiting for events: event logging is %s\n", logevents ? "on" : "off"); while (1) { struct pollfd ar[2]; int r; int fds = 0; /* poll for the socket and the event file */ ar[0].fd = event_fd; ar[0].events = POLLIN; fds++; if (!nosocket) { ar[1].fd = sock_fd; ar[1].events = POLLIN; fds++; } r = poll(ar, fds, -1); if (r < 0 && errno == EINTR) { continue; } else if (r < 0) { acpid_log(LOG_ERR, "poll(): %s\n", strerror(errno)); continue; } /* house keeping */ acpid_close_dead_clients(); /* was it an event? */ if (ar[0].revents) { char *event; struct stat trash; int fexists; /* check for existence of a lockfile */ fexists = (stat(lockfile, &trash) == 0); /* this shouldn't happen */ if (!ar[0].revents & POLLIN) { acpid_log(LOG_DEBUG, "odd, poll set flags 0x%x\n", ar[0].revents); continue; } /* read an event */ event = read_line(event_fd); /* if we're locked, don't process the event */ if (fexists) { if (logevents) { acpid_log(LOG_INFO, "lockfile present, not processing " "event \"%s\"\n", event); } continue; } /* handle the event */ if (event) { if (logevents) { acpid_log(LOG_INFO, "received event \"%s\"\n", event); } acpid_handle_event(event); if (logevents) { acpid_log(LOG_INFO, "completed event \"%s\"\n", event); } } else if (errno == EPIPE) { acpid_log(LOG_WARNING, "events file connection closed\n"); break; } else { static int nerrs; if (++nerrs >= ACPID_MAX_ERRS) { acpid_log(LOG_ERR, "too many errors reading " "events file - aborting\n"); break; } } } /* was it a new connection? */ if (!nosocket && ar[1].revents) { int cli_fd; struct ucred creds; char buf[32]; static int accept_errors; /* this shouldn't happen */ if (!ar[1].revents & POLLIN) { acpid_log(LOG_DEBUG, "odd, poll set flags 0x%x\n", ar[1].revents); continue; } /* accept and add to our lists */ cli_fd = ud_accept(sock_fd, &creds); if (cli_fd < 0) { acpid_log(LOG_ERR, "can't accept client: %s\n", strerror(errno)); accept_errors++; if (accept_errors >= 5) { acpid_log(LOG_ERR, "giving up\n"); clean_exit_with_status(EXIT_FAILURE); } continue; } accept_errors = 0; if (creds.uid != 0 && non_root_clients >= clientmax) { close(cli_fd); acpid_log(LOG_ERR, "too many non-root clients\n"); continue; } if (creds.uid != 0) { non_root_clients++; } fcntl(cli_fd, F_SETFD, FD_CLOEXEC); snprintf(buf, sizeof(buf)-1, "%d[%d:%d]", creds.pid, creds.uid, creds.gid); acpid_add_client(cli_fd, buf); } } clean_exit_with_status(EXIT_SUCCESS); return 0; }
/* called when an inotify event is received */ static void process_inotify(int fd) { int bytes; /* union to avoid strict-aliasing problems */ union { char buffer[256]; /* a tad large */ struct inotify_event event; } eventbuf; bytes = read(fd, &eventbuf.buffer, sizeof(eventbuf.buffer)); acpid_log(LOG_DEBUG, "inotify read bytes: %d", bytes); /* eof is not expected */ if (bytes == 0) { acpid_log(LOG_WARNING, "inotify fd eof encountered"); return; } else if (bytes < 0) { /* EINVAL means buffer wasn't big enough. See inotify(7). */ acpid_log(LOG_ERR, "inotify read error: %s (%d)", strerror(errno), errno); acpid_log(LOG_ERR, "disconnecting from inotify"); delete_connection(fd); return; } acpid_log(LOG_DEBUG, "inotify name len: %d", eventbuf.event.len); const int dnsize = 256; char devname[dnsize]; /* if a name is included */ if (eventbuf.event.len > 0) { /* devname = ACPID_INPUTLAYERDIR + "/" + pevent -> name */ strcpy(devname, ACPID_INPUTLAYERDIR); strcat(devname, "/"); strncat(devname, eventbuf.event.name, dnsize - strlen(devname) - 1); } /* if this is a create */ if (eventbuf.event.mask & IN_CREATE) { acpid_log(LOG_DEBUG, "inotify about to open: %s", devname); open_inputfile(devname); } /* if this is a delete */ if (eventbuf.event.mask & IN_DELETE) { /* struct connection *c; */ acpid_log(LOG_DEBUG, "inotify received a delete for: %s", devname); #if 0 /* Switching back to the original ENODEV detection scheme. See process_input() in input_layer.c. */ /* keeping this for future reference */ /* search for the event file in the connection list */ /* ??? Or should we just have a delete_connection_name()? */ c = find_connection_name(devname); /* close that connection if found */ if (c) delete_connection(c->fd); #endif } }