OSDN Git Service

c20071dcead4a6c67e94bf91f9835b8f32546af3
[openpts/openpts.git] / src / ptsevtd.c
1 /*
2  * This file is part of the OpenPTS project.
3  *
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.
8  *
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)
12  * any later version.
13  *
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.
18  *
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.
22  */
23
24 /**
25  * \file src/ptsevt.c
26  * \brief doorbell listener
27  * @author David Sherwood <davidshe@uk.ibm.com>
28  * @date 2011-09-27
29  * cleanup 2011-10-07 SM
30  */
31
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/time.h>
35 #include <sys/wait.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <netdb.h>
42 #include <poll.h>
43 #include <signal.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <syslog.h>
49 #include <time.h>
50 #include <unistd.h>
51
52 #include <openpts_log.h>
53 #include <ptsevt_msg.h>
54
55 #define MAXLISTEN      10        /* max addresses to listen on */
56 #define MAXPEER        0x1000    /* max connections to accept */
57 #define MAXCHLD        10        /* max child processes */
58
59 /*
60  * the state machine for the lifetime of an event
61  */
62 struct peer {
63     struct peer *next;
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 */
80 };
81
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 */
91
92 /*
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
96  */
97 void vlogn(unsigned msglevel, char *fmt, va_list ap) {
98 #define LOGMAX    160
99     char buf[LOGMAX];
100
101     if (msglevel > loglevel)
102         return;
103     vsnprintf(buf, LOGMAX, fmt, ap);
104     va_end(ap);
105     if (foreground) {
106         fprintf(stderr, "%s\n", buf);
107         fflush(stderr);
108     } else {
109         syslog(msglevel ? LOG_INFO : LOG_CRIT, "%s", buf);
110     }
111 }
112
113 /*
114  * log a fatal error and exit
115  */
116 void log_err(char *fmt, ...) {
117     va_list ap;
118
119     va_start(ap, fmt);
120     vlogn(0, fmt, ap);
121     va_end(ap);
122     exit(1);
123 }
124
125 /*
126  * log an error
127  */
128 void log_warn(char *fmt, ...) {
129     va_list ap;
130
131     va_start(ap, fmt);
132     vlogn(0, fmt, ap);
133     va_end(ap);
134 }
135
136 /*
137  * log a debug message (if the log level is verbose enough)
138  */
139 void log_debug(char *fmt, ...) {
140     va_list ap;
141
142     va_start(ap, fmt);
143     vlogn(2, fmt, ap);
144     va_end(ap);
145 }
146
147 /*
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.
152  */
153 void peer_accept(int fd) {
154     struct peer *p;
155     struct sockaddr_storage caddr;
156     socklen_t clen;
157     int s, gai_err;
158     char addr[NI_MAXHOST + 1];
159
160     clen = sizeof(struct sockaddr_storage);
161     s = accept(fd, (struct sockaddr *)&caddr, &clen);
162     if (s < 0) {
163         log_warn("accept: %s", strerror(errno));
164         return;
165     }
166     gai_err = getnameinfo((struct sockaddr *)&caddr, clen, addr,
167         NI_MAXHOST, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV);
168     if (gai_err) {
169         log_warn("getnameinfo: %s", gai_strerror(gai_err));
170         return;
171     }
172     if (npeer == MAXPEER) {
173         log_warn("too many connections, %s rejected", addr);
174         return;
175     }
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)
180                 p->restart = 1;
181             log_debug("%s: already in progress", addr);
182             return;
183         }
184     }
185     p = malloc(sizeof(struct peer));
186     if (p == NULL) {
187         log_warn("%s: malloc: %s", addr, strerror(errno));
188         return;
189     }
190     p->fd = s;
191     p->msglen = 0;
192     p->state = PEER_RECV;
193     p->pfd = NULL;
194     p->timeout = TIMO_RECV;
195     p->restart = 0;
196     strncpy(p->addr, addr, NI_MAXHOST + 1);
197     p->next = peerlist;
198     peerlist = p;
199     npeer++;
200     log_debug("%s: connected", p->addr);
201 }
202
203 /*
204  * remove a peer structure and free any resources
205  */
206 void peer_del(struct peer *p) {
207     struct peer **i;
208
209     for (i = &peerlist; *i != 0; i = &(*i)->next) {
210         if (*i == p) {
211             *i = p->next;
212             close(p->fd);
213             free(p);
214             npeer--;
215             return;
216         }
217     }
218     log_err("peer_del: peer not found");
219 }
220
221 /*
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
225  */
226 void peer_fork(struct peer *p) {
227     if (nchild >= MAXCHLD) {
228         p->state = PEER_FORK;
229         return;
230     }
231     p->pid = fork();
232     if (p->pid < 0) {
233         log_warn("%s: fork: %s", p->addr, strerror(errno));
234         p->state = PEER_ENDING;
235         p->timeout = TIMO_ENDING;
236         p->restart = 1;
237         return;
238     }
239     if (p->pid == 0) {
240         execlp(command, command, p->msg.uuid, (char *)NULL);
241         log_err("%s: exec of %s: %s", p->addr, command, strerror(errno));
242     }
243     log_debug("%s: forked pid %d", p->addr, p->pid);
244     p->state = PEER_WAITPID;
245     p->timeout = TIMO_WAITPID;
246     nchild++;
247 }
248
249 /*
250  * receive bytes from the socket, as soon as the message is complete,
251  * attempt to fork() a child process
252  */
253 void peer_recv(struct peer *p) {
254     unsigned char *buf, *endl;
255     int n;
256
257     buf = (unsigned char *)&p->msg;
258     buf += p->msglen;
259     n = read(p->fd, buf, sizeof(struct msg) - p->msglen);
260     if (n < 0) {
261         log_warn("%s: read failed: %s", p->addr, strerror(errno));
262         peer_del(p);
263         return;
264     } else if (n == 0) {
265         peer_del(p);
266         return;
267     }
268     p->msglen += n;
269     if (p->msglen == sizeof(struct msg)) {
270         endl = memchr(p->msg.uuid, '\0', MSG_UUIDMAX);
271         if (endl == NULL) {
272             log_warn("%s: corrupted uuid", p->addr);
273             peer_del(p);
274             return;
275         }
276         peer_fork(p);
277     }
278 }
279
280 /*
281  * the timeout expired, move to the next state
282  */
283 void peer_timeout(struct peer *p) {
284     switch (p->state) {
285     case PEER_RECV:
286         peer_del(p);
287         break;
288     case PEER_WAITPID:
289         log_debug("%s: pid %d killed", p->addr, p->pid);
290         p->timeout = TIMO_WAITPID;
291         kill(p->pid, SIGKILL);
292         break;
293     case PEER_ENDING:
294         if (p->restart) {
295             p->restart = 0;
296             peer_fork(p);
297             break;
298         }
299         log_debug("%s: expired", p->addr);
300         peer_del(p);
301         break;
302     }
303 }
304
305 /*
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
308  */
309 void
310 dummy(int s) {
311 }
312
313 int
314 main(int argc, char **argv) {
315     pid_t pid;
316     sigset_t set;
317     struct sigaction sa;
318     struct itimerval it;
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;
325     unsigned i;
326     int f_flag = 0;
327
328         initCatalog();
329
330     while ((c = getopt(argc, argv, "c:dfp:")) != -1) {
331         switch (c) {
332         case 'c':
333             command = optarg;
334             break;
335         case 'd':
336             if (loglevel < 2)
337                 loglevel++;
338             break;
339         case 'f':
340             f_flag = 1;
341             break;
342         case 'p':
343             port = optarg;
344             break;
345         default:
346             goto usage;
347         }
348     }
349     argc -= optind;
350     argv += optind;
351
352     if (argc > 0) {
353     usage:
354         fprintf(stderr, NLS(MS_OPENPTS, OPENPTS_PTSEVTD_USAGE,
355             "syntax: ptsevtd [-df] [-p port] [-c command]\n"));
356         exit(1);
357     }
358
359     /*
360      * block SIGPIPE, overwise write() may kill the process
361      */
362     sigemptyset(&set);
363     sigaddset(&set, SIGPIPE);
364     if (sigprocmask(SIG_BLOCK, &set, NULL))
365         log_err("sigprocmask: %s", strerror(errno));
366
367     /*
368      * install a dummy handler for SIGCHLD and SIGALRM
369      */
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));
377
378     /*
379      * listen on default address
380      */
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);
386     if (gai_err)
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);
390         if (s < 0)
391             log_err("socket: %s", strerror(errno));
392         opt = 1;
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) {
396             save_errno = errno;
397             close(s);
398             continue;
399         }
400         if (listen(s, 1) < 0)
401             log_err("listen: %s", strerror(errno));
402         listen_fd[nlisten++] = s;
403     }
404     freeaddrinfo(ailist);
405
406     if (nlisten == 0)
407         log_err("bind: %s", strerror(save_errno));
408
409     /*
410      * daemonize
411      */
412     if (!f_flag) {
413         pid = fork();
414         if (pid < 0)
415             log_err("fork: %s", strerror(errno));
416         if (pid > 0)
417             _exit(0);
418         foreground = 0;
419         openlog("ptsevtd", LOG_PID, LOG_DAEMON);
420         setsid();
421         fd = open("/dev/null", O_RDWR, 0666);
422         if (fd < 0)
423             log_err("/dev/null: %s\n", strerror(errno));
424         dup2(fd, STDIN_FILENO);
425         dup2(fd, STDOUT_FILENO);
426         dup2(fd, STDERR_FILENO);
427         if (fd > 2)
428             close(fd);
429     }
430
431     /*
432      * start periodic timer
433      */
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));
442
443     /*
444      * main loop
445      */
446     for (;;) {
447         /*
448          * fill table for descriptor to poll
449          */
450         pfd = pfds;
451         for (i = 0; i < nlisten; i++) {
452             pfd->fd = listen_fd[i];
453             pfd->events = POLLIN;
454             pfd++;
455         }
456         for (p = peerlist; p != NULL; p = p->next) {
457             if (p->state != PEER_RECV)
458                 continue;
459             pfd->fd = p->fd;
460             pfd->events = POLLIN;
461             p->pfd = pfd;
462             pfd++;
463         }
464
465         /*
466          * wait
467          */
468         res = poll(pfds, pfd - pfds, -1);
469         if (res < 0 && errno != EINTR)
470             log_err("poll: %s", strerror(errno));
471
472         /*
473          * scan descriptors that have changed
474          */
475         if (res > 0) {
476             pfd = pfds;
477             for (i = 0; i < nlisten; i++) {
478                 if (pfd->revents & POLLIN)
479                     peer_accept(pfd->fd);
480                 pfd++;
481             }
482             for (p = peerlist; p != NULL; p = pnext) {
483                 pnext = p->next;
484                 if (p->state != PEER_RECV || p->pfd == NULL)
485                     continue;
486                 if (p->pfd->revents & POLLIN)
487                     peer_recv(p);
488             }
489         }
490
491         /*
492          * scan for terminated childs
493          */
494         pid = waitpid(-1, &status, WNOHANG);
495         if (pid > 0) {
496             i = 0;
497             for (p = peerlist; p != NULL; p = pnext) {
498                 pnext = p->next;
499                 if (p->state != PEER_WAITPID)
500                     continue;
501                 if (p->pid != pid)
502                     continue;
503                 log_debug("%s: pid %d reaped", p->addr, p->pid);
504                 nchild--;
505                 p->state = PEER_ENDING;
506                 p->timeout = TIMO_ENDING;
507                 i++;
508             }
509             for (p = peerlist; i > 0 && p != NULL; p = pnext) {
510                 pnext = p->next;
511                 if (p->state != PEER_FORK)
512                     continue;
513                 peer_fork(p);
514                 i--;
515             }
516         }
517
518         /*
519          * advance timeouts
520          */
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;
525         if (delta > 0) {
526             ts_last = ts;
527             for (p = peerlist; p != NULL; p = pnext) {
528                 pnext = p->next;
529                 if (p->timeout > delta) {
530                     p->timeout -= delta;
531                     continue;
532                 }
533                 peer_timeout(p);
534             }
535         }
536     }
537     return 0;
538 }