2 * This file is part of the OpenPTS project.
4 * The Initial Developer of the Original Code is International
5 * Business Machines Corporation. Portions created by IBM
6 * Corporation are Copyright (C) 2011 International Business
7 * Machines Corporation. All Rights Reserved.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the Common Public License as published by
11 * IBM Corporation; either version 1 of the License, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * Common Public License for more details.
19 * You should have received a copy of the Common Public License
20 * along with this program; if not, a copy can be viewed at
21 * http://www.opensource.org/licenses/cpl1.0.php.
26 * \brief doorbell listener
27 * @author David Sherwood <davidshe@uk.ibm.com>
29 * cleanup 2011-10-07 SM
32 #include <sys/types.h>
33 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
52 #include <openpts_log.h>
53 #include <ptsevt_msg.h>
55 #define MAXLISTEN 10 /* max addresses to listen on */
56 #define MAXPEER 0x1000 /* max connections to accept */
57 #define MAXCHLD 10 /* max child processes */
60 * the state machine for the lifetime of an event
64 int fd; /* incoming connection socket */
65 struct pollfd *pfd; /* array entry for poll() */
66 char addr[NI_MAXHOST + 1]; /* string with source IP/IPv6 */
67 #define PEER_RECV 0 /* reading the message */
68 #define PEER_FORK 1 /* too many childs to fork, waiting */
69 #define PEER_WAITPID 2 /* waiting the child to terminate */
70 #define PEER_ENDING 3 /* done, keeping track of the evt */
71 int state; /* one of above */
72 #define TIMO_RECV 5000000000LL /* ns before we drop connection */
73 #define TIMO_WAITPID 60000000000LL /* ns before we kill the child */
74 #define TIMO_ENDING 3000000000LL /* ns between fork()'s */
75 int64_t timeout; /* expires when 0 is reached */
76 int restart; /* if 1, fork() openpts again */
77 pid_t pid; /* openpts */
78 unsigned msglen; /* bytes received */
79 struct msg msg; /* actual message */
82 int listen_fd[MAXLISTEN]; /* sockets we're listening on */
83 unsigned nlisten; /* number of sockets we're listening on */
84 struct peer *peerlist = NULL; /* list of events being processed */
85 unsigned npeer; /* number of connections */
86 unsigned nchild; /* number of child processes */
87 unsigned loglevel = 0; /* 0 = err, 1 = warn, 2 = debug */
88 unsigned foreground = 1; /* don't become a daemon */
89 char *command = "openpts"; /* command to run on each event */
90 char *port = MSG_PORT; /* port to listen on */
93 * log using syslog or stderr depending whether the program is
94 * deamonized or not. Messages have "priorities", and are actually
95 * printed only of the log level is high enough
97 void vlogn(unsigned msglevel, char *fmt, va_list ap) {
101 if (msglevel > loglevel)
103 vsnprintf(buf, LOGMAX, fmt, ap);
106 fprintf(stderr, "%s\n", buf);
109 syslog(msglevel ? LOG_INFO : LOG_CRIT, "%s", buf);
114 * log a fatal error and exit
116 void log_err(char *fmt, ...) {
128 void log_warn(char *fmt, ...) {
137 * log a debug message (if the log level is verbose enough)
139 void log_debug(char *fmt, ...) {
148 * accept a new connection from the given socket, allocate its peer
149 * structure in the PEER_RECV state and add it to the list of peers. If
150 * a peer structure already exists for the source address, then its used
151 * and no new structure is allocated.
153 void peer_accept(int fd) {
155 struct sockaddr_storage caddr;
158 char addr[NI_MAXHOST + 1];
160 clen = sizeof(struct sockaddr_storage);
161 s = accept(fd, (struct sockaddr *)&caddr, &clen);
163 log_warn("accept: %s", strerror(errno));
166 gai_err = getnameinfo((struct sockaddr *)&caddr, clen, addr,
167 NI_MAXHOST, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV);
169 log_warn("getnameinfo: %s", gai_strerror(gai_err));
172 if (npeer == MAXPEER) {
173 log_warn("too many connections, %s rejected", addr);
176 for (p = peerlist; p != NULL; p = p->next) {
177 if (strcmp(addr, p->addr) == 0) {
178 if (p->state == PEER_WAITPID ||
179 p->state == PEER_ENDING)
181 log_debug("%s: already in progress", addr);
185 p = malloc(sizeof(struct peer));
187 log_warn("%s: malloc: %s", addr, strerror(errno));
192 p->state = PEER_RECV;
194 p->timeout = TIMO_RECV;
196 strncpy(p->addr, addr, NI_MAXHOST + 1);
200 log_debug("%s: connected", p->addr);
204 * remove a peer structure and free any resources
206 void peer_del(struct peer *p) {
209 for (i = &peerlist; *i != 0; i = &(*i)->next) {
218 log_err("peer_del: peer not found");
222 * attempt to fork() and exec() a command. If there
223 * are too many childs running, then switch to the
224 * PEER_FORK state to wait
226 void peer_fork(struct peer *p) {
227 if (nchild >= MAXCHLD) {
228 p->state = PEER_FORK;
233 log_warn("%s: fork: %s", p->addr, strerror(errno));
234 p->state = PEER_ENDING;
235 p->timeout = TIMO_ENDING;
240 execlp(command, command, p->msg.uuid, (char *)NULL);
241 log_err("%s: exec of %s: %s", p->addr, command, strerror(errno));
243 log_debug("%s: forked pid %d", p->addr, p->pid);
244 p->state = PEER_WAITPID;
245 p->timeout = TIMO_WAITPID;
250 * receive bytes from the socket, as soon as the message is complete,
251 * attempt to fork() a child process
253 void peer_recv(struct peer *p) {
254 unsigned char *buf, *endl;
257 buf = (unsigned char *)&p->msg;
259 n = read(p->fd, buf, sizeof(struct msg) - p->msglen);
261 log_warn("%s: read failed: %s", p->addr, strerror(errno));
269 if (p->msglen == sizeof(struct msg)) {
270 endl = memchr(p->msg.uuid, '\0', MSG_UUIDMAX);
272 log_warn("%s: corrupted uuid", p->addr);
281 * the timeout expired, move to the next state
283 void peer_timeout(struct peer *p) {
289 log_debug("%s: pid %d killed", p->addr, p->pid);
290 p->timeout = TIMO_WAITPID;
291 kill(p->pid, SIGKILL);
299 log_debug("%s: expired", p->addr);
306 * dummy signal handler. We have nothing to do here, but we must use a
307 * signal handler for poll() to be interrupted by SIGALRM or SIGCHLD
314 main(int argc, char **argv) {
319 struct peer *p, *pnext;
320 struct pollfd *pfd, pfds[MAXPEER + MAXLISTEN];
321 struct addrinfo *ailist, *ai, aihints;
322 struct timespec ts, ts_last;
323 int64_t delta; // long long
324 int fd, c, res, status, s, gai_err, save_errno = 0, opt;
330 while ((c = getopt(argc, argv, "c:dfp:")) != -1) {
354 fprintf(stderr, NLS(MS_OPENPTS, OPENPTS_PTSEVTD_USAGE,
355 "syntax: ptsevtd [-df] [-p port] [-c command]\n"));
360 * block SIGPIPE, overwise write() may kill the process
363 sigaddset(&set, SIGPIPE);
364 if (sigprocmask(SIG_BLOCK, &set, NULL))
365 log_err("sigprocmask: %s", strerror(errno));
368 * install a dummy handler for SIGCHLD and SIGALRM
370 sa.sa_flags = SA_RESTART;
371 sa.sa_handler = dummy;
372 sigfillset(&sa.sa_mask);
373 if (sigaction(SIGCHLD, &sa, NULL) < 0)
374 log_err("sigaction: %s", strerror(errno));
375 if (sigaction(SIGALRM, &sa, NULL) < 0)
376 log_err("sigaction: %s", strerror(errno));
379 * listen on default address
381 memset(&aihints, 0, sizeof(struct addrinfo));
382 aihints.ai_flags = AI_PASSIVE;
383 aihints.ai_family = AF_UNSPEC;
384 aihints.ai_socktype = SOCK_STREAM;
385 gai_err = getaddrinfo(NULL, port, &aihints, &ailist);
387 log_err("getaddrinfo: %s", gai_strerror(gai_err));
388 for (ai = ailist; ai != NULL; ai = ai->ai_next) {
389 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
391 log_err("socket: %s", strerror(errno));
393 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0)
394 log_err("setsockopt: %s", strerror(errno));
395 if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0) {
400 if (listen(s, 1) < 0)
401 log_err("listen: %s", strerror(errno));
402 listen_fd[nlisten++] = s;
404 freeaddrinfo(ailist);
407 log_err("bind: %s", strerror(save_errno));
415 log_err("fork: %s", strerror(errno));
419 openlog("ptsevtd", LOG_PID, LOG_DAEMON);
421 fd = open("/dev/null", O_RDWR, 0666);
423 log_err("/dev/null: %s\n", strerror(errno));
424 dup2(fd, STDIN_FILENO);
425 dup2(fd, STDOUT_FILENO);
426 dup2(fd, STDERR_FILENO);
432 * start periodic timer
434 it.it_interval.tv_sec = 0;
435 it.it_interval.tv_usec = 100000;
436 it.it_value.tv_sec = 0;
437 it.it_value.tv_usec = 100000;
438 if (setitimer(ITIMER_REAL, &it, NULL) < 0)
439 log_err("setitimer: %s", strerror(errno));
440 if (clock_gettime(CLOCK_MONOTONIC, &ts_last) < 0)
441 log_err("clock_gettime: %s", strerror(errno));
448 * fill table for descriptor to poll
451 for (i = 0; i < nlisten; i++) {
452 pfd->fd = listen_fd[i];
453 pfd->events = POLLIN;
456 for (p = peerlist; p != NULL; p = p->next) {
457 if (p->state != PEER_RECV)
460 pfd->events = POLLIN;
468 res = poll(pfds, pfd - pfds, -1);
469 if (res < 0 && errno != EINTR)
470 log_err("poll: %s", strerror(errno));
473 * scan descriptors that have changed
477 for (i = 0; i < nlisten; i++) {
478 if (pfd->revents & POLLIN)
479 peer_accept(pfd->fd);
482 for (p = peerlist; p != NULL; p = pnext) {
484 if (p->state != PEER_RECV || p->pfd == NULL)
486 if (p->pfd->revents & POLLIN)
492 * scan for terminated childs
494 pid = waitpid(-1, &status, WNOHANG);
497 for (p = peerlist; p != NULL; p = pnext) {
499 if (p->state != PEER_WAITPID)
503 log_debug("%s: pid %d reaped", p->addr, p->pid);
505 p->state = PEER_ENDING;
506 p->timeout = TIMO_ENDING;
509 for (p = peerlist; i > 0 && p != NULL; p = pnext) {
511 if (p->state != PEER_FORK)
521 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
522 log_err("clock_gettime: %s", strerror(errno));
523 delta = 1000000000LL * (ts.tv_sec - ts_last.tv_sec);
524 delta += ts.tv_nsec - ts_last.tv_nsec;
527 for (p = peerlist; p != NULL; p = pnext) {
529 if (p->timeout > delta) {