1 /*
2    Copyright (C) 2003  Ferdi Franceschini <ferdif@optusnet.com.au>
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*! \file
19  * Main functions to set-up dpi information and to initialise sockets
20  */
21 #include <errno.h>
22 #include <stdlib.h>             /* for exit */
23 #include <fcntl.h>              /* for F_SETFD, F_GETFD, FD_CLOEXEC */
24 
25 #include <sys/stat.h>
26 #include <sys/wait.h>
27 #include <sys/socket.h>
28 #include <netinet/tcp.h>
29 
30 #include <unistd.h>
31 #include "dpid_common.h"
32 #include "dpid.h"
33 #include "dpi.h"
34 #include "dpi_socket_dir.h"
35 #include "misc_new.h"
36 
37 #include "../dpip/dpip.h"
38 
39 #define QUEUE 5
40 
41 volatile sig_atomic_t caught_sigchld = 0;
42 char *SharedKey = NULL;
43 
44 /*! Remove dpid_comm_keys file.
45  * This avoids that dillo instances connect to a stale port after dpid
46  * has exited (e.g. after a reboot).
47  */
cleanup()48 void cleanup()
49 {
50    char *fname;
51    fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
52    unlink(fname);
53    dFree(fname);
54 }
55 
56 /*! Free memory used to describe
57  * a set of dpi attributes
58  */
free_dpi_attr(struct dp * dpi_attr)59 void free_dpi_attr(struct dp *dpi_attr)
60 {
61    if (dpi_attr->id != NULL) {
62       dFree(dpi_attr->id);
63       dpi_attr->id = NULL;
64    }
65    if (dpi_attr->path != NULL) {
66       dFree(dpi_attr->path);
67       dpi_attr->path = NULL;
68    }
69 }
70 
71 /*! Free memory used by the plugin list
72  */
free_plugin_list(struct dp ** dpi_attr_list_ptr,int numdpis)73 void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis)
74 {
75    int i;
76    struct dp *dpi_attr_list = *dpi_attr_list_ptr;
77 
78    if (dpi_attr_list == NULL)
79       return;
80 
81    for (i = 0; i < numdpis; i++)
82       free_dpi_attr(dpi_attr_list + i);
83 
84    dFree(dpi_attr_list);
85    *dpi_attr_list_ptr = NULL;
86 }
87 
88 /*! Free memory used by the services list
89  */
free_services_list(Dlist * s_list)90 void free_services_list(Dlist *s_list)
91 {
92    int i = 0;
93    struct service *s;
94 
95    for (i=0; i < dList_length(s_list) ; i++) {
96       s = dList_nth_data(s_list, i);
97       dFree(s->name);
98    }
99    dList_free(s_list);
100 }
101 
102 /*! Signal handler for SIGINT, SIGQUIT, and SIGTERM. Calls cleanup
103  */
terminator(int sig)104 static void terminator(int sig)
105 {
106    (void) sig; /* suppress unused parameter warning */
107    cleanup();
108    _exit(0);
109 }
110 
111 /*! Establish handler for termination signals
112  * and register cleanup with atexit */
est_dpi_terminator()113 void est_dpi_terminator()
114 {
115    struct sigaction act;
116    sigset_t block;
117 
118    sigemptyset(&block);
119    sigaddset(&block, SIGHUP);
120    sigaddset(&block, SIGINT);
121    sigaddset(&block, SIGQUIT);
122    sigaddset(&block, SIGTERM);
123 
124    act.sa_handler = terminator;
125    act.sa_mask = block;
126    act.sa_flags = 0;
127 
128    if (sigaction(SIGHUP, &act, NULL) ||
129        sigaction(SIGINT, &act, NULL) ||
130        sigaction(SIGQUIT, &act, NULL) ||
131        sigaction(SIGTERM, &act, NULL)) {
132       ERRMSG("est_dpi_terminator", "sigaction", errno);
133       exit(1);
134    }
135 
136    if (atexit(cleanup) != 0) {
137       ERRMSG("est_dpi_terminator", "atexit", 0);
138       MSG_ERR("Hey! atexit failed, how did that happen?\n");
139       exit(1);
140    }
141 }
142 
143 /*! Identify a given file
144  * Currently there is only one file type associated with dpis.
145  * More file types will be added as needed
146  */
get_file_type(char * file_name)147 enum file_type get_file_type(char *file_name)
148 {
149    char *dot = strrchr(file_name, '.');
150 
151    if (dot && !strcmp(dot, ".dpi"))
152       return DPI_FILE;             /* Any filename ending in ".dpi" */
153    else {
154       MSG_ERR("get_file_type: Unknown file type for %s\n", file_name);
155       return UNKNOWN_FILE;
156    }
157 }
158 
159 /*! Get dpi directory path from dpidrc
160  * \Return
161  * dpi directory on success, NULL on failure
162  * \Important
163  * The dpi_dir definition in dpidrc must have no leading white space.
164  */
get_dpi_dir(char * dpidrc)165 char *get_dpi_dir(char *dpidrc)
166 {
167    FILE *In;
168    int len;
169    char *rcline = NULL, *value = NULL, *p;
170 
171    if ((In = fopen(dpidrc, "r")) == NULL) {
172       ERRMSG("dpi_dir", "fopen", errno);
173       MSG_ERR(" - %s\n", dpidrc);
174       return (NULL);
175    }
176 
177    while ((rcline = dGetline(In)) != NULL) {
178       if (strncmp(rcline, "dpi_dir", 7) == 0)
179          break;
180       dFree(rcline);
181    }
182    fclose(In);
183 
184    if (!rcline) {
185       ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0);
186       MSG_ERR("Put your dillo plugins path in %s\n", dpidrc);
187       MSG_ERR("e.g. dpi_dir=/usr/local/libexec/dillo/dpi\n");
188       MSG_ERR("with no leading spaces.\n");
189       value = NULL;
190    } else {
191       len = (int) strlen(rcline);
192       if (len && rcline[len - 1] == '\n')
193          rcline[len - 1] = 0;
194 
195       if ((p = strchr(rcline, '='))) {
196          while (*++p == ' ');
197          value = dStrdup(p);
198       } else {
199          ERRMSG("dpi_dir", "strchr", 0);
200          MSG_ERR(" - '=' not found in %s\n", rcline);
201          value = NULL;
202       }
203    }
204 
205    dFree(rcline);
206    return (value);
207 }
208 
209 /*! Scans a service directory in dpi_dir and fills dpi_attr
210  * \Note
211  * Caller must allocate memory for dpi_attr.
212  * \Return
213  * \li 0 on success
214  * \li -1 on failure
215  * \todo
216  * Add other file types, but first we need to add files associated with a dpi
217  * to the design.
218  */
get_dpi_attr(char * dpi_dir,char * service,struct dp * dpi_attr)219 int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr)
220 {
221    char *service_dir = NULL;
222    struct stat statinfo;
223    enum file_type ftype;
224    int ret = -1;
225    DIR *dir_stream;
226    struct dirent *dir_entry = NULL;
227 
228    service_dir = dStrconcat(dpi_dir, "/", service, NULL);
229    if (stat(service_dir, &statinfo) == -1) {
230       ERRMSG("get_dpi_attr", "stat", errno);
231       MSG_ERR("file=%s\n", service_dir);
232    } else if ((dir_stream = opendir(service_dir)) == NULL) {
233       ERRMSG("get_dpi_attr", "opendir", errno);
234    } else {
235       /* Scan the directory looking for dpi files.
236        * (currently there's only the dpi program, but in the future
237        *  there may also be helper scripts.) */
238       while ( (dir_entry = readdir(dir_stream)) != NULL) {
239          if (dir_entry->d_name[0] == '.')
240             continue;
241 
242          ftype = get_file_type(dir_entry->d_name);
243          switch (ftype) {
244             case DPI_FILE:
245                dpi_attr->path =
246                   dStrconcat(service_dir, "/", dir_entry->d_name, NULL);
247                dpi_attr->id = dStrdup(service);
248                dpi_attr->port = 0;
249                dpi_attr->pid = 1;
250                if (strstr(dpi_attr->path, ".filter") != NULL)
251                   dpi_attr->filter = 1;
252                else
253                   dpi_attr->filter = 0;
254                ret = 0;
255                break;
256             default:
257                break;
258          }
259       }
260       closedir(dir_stream);
261 
262       if (ret != 0)
263          MSG_ERR("get_dpi_attr: No dpi plug-in in %s/%s\n",
264                  dpi_dir, service);
265    }
266    dFree(service_dir);
267    return ret;
268 }
269 
270 /*! Register a service
271  * Retrieves attributes for "service" and stores them
272  * in dpi_attr. It looks for "service" in ~/.dillo/dpi
273  * first, and then in the system wide dpi directory.
274  * Caller must allocate memory for dpi_attr.
275  * \Return
276  * \li 0 on success
277  * \li -1 on failure
278  */
register_service(struct dp * dpi_attr,char * service)279 int register_service(struct dp *dpi_attr, char *service)
280 {
281    char *user_dpi_dir, *dpidrc, *user_service_dir, *dir = NULL;
282    int ret = -1;
283 
284    user_dpi_dir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
285    user_service_dir =
286        dStrconcat(dGethomedir(), "/", dotDILLO_DPI, "/", service, NULL);
287 
288    dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
289    if (access(dpidrc, F_OK) == -1) {
290       if (access(DPIDRC_SYS, F_OK) == -1) {
291          ERRMSG("register_service", "Error ", 0);
292          MSG_ERR("\n - There is no %s or %s file\n", dpidrc,
293                DPIDRC_SYS);
294          dFree(user_dpi_dir);
295          dFree(user_service_dir);
296          dFree(dpidrc);
297          return(-1);
298       }
299       dFree(dpidrc);
300       dpidrc = dStrdup(DPIDRC_SYS);
301    }
302 
303    /* Check home dir for dpis */
304    if (access(user_service_dir, F_OK) == 0) {
305       get_dpi_attr(user_dpi_dir, service, dpi_attr);
306       ret = 0;
307    } else {                     /* Check system wide dpis */
308       if ((dir = get_dpi_dir(dpidrc)) != NULL) {
309          if (access(dir, F_OK) == 0) {
310             get_dpi_attr(dir, service, dpi_attr);
311             ret = 0;
312          } else {
313             ERRMSG("register_service", "get_dpi_attr failed", 0);
314          }
315       } else {
316          ERRMSG("register_service", "dpi_dir: Error getting dpi dir.", 0);
317       }
318    }
319    dFree(user_dpi_dir);
320    dFree(user_service_dir);
321    dFree(dpidrc);
322    dFree(dir);
323    return ret;
324 }
325 
326 /*!
327  * Create dpi directory for available
328  * plugins and create plugin list.
329  * \Return
330  * \li Returns number of available plugins on success
331  * \li -1 on failure
332  */
register_all(struct dp ** attlist)333 int register_all(struct dp **attlist)
334 {
335    DIR *user_dir_stream, *sys_dir_stream;
336    char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
337    struct dirent *user_dirent, *sys_dirent;
338    int st;
339    int snum;
340    size_t dp_sz = sizeof(struct dp);
341 
342    if (*attlist != NULL) {
343       ERRMSG("register_all", "attlist parameter should be NULL", 0);
344       return -1;
345    }
346 
347    user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
348    if (access(user_dpidir, F_OK) == -1) {
349       /* no dpis in user's space */
350       dFree(user_dpidir);
351       user_dpidir = NULL;
352    }
353    dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
354    if (access(dpidrc, F_OK) == -1) {
355       dFree(dpidrc);
356       dpidrc = dStrdup(DPIDRC_SYS);
357       if (access(dpidrc, F_OK) == -1) {
358          dFree(dpidrc);
359          dpidrc = NULL;
360       }
361    }
362    if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
363       sys_dpidir = NULL;
364    dFree(dpidrc);
365 
366    if (!user_dpidir && !sys_dpidir) {
367       ERRMSG("register_all", "Fatal error ", 0);
368       MSG_ERR("\n - Can't find the directory for dpis.\n");
369       exit(1);
370    }
371 
372    /* Get list of services in user's .dillo/dpi directory */
373    snum = 0;
374    if (user_dpidir && (user_dir_stream = opendir(user_dpidir)) != NULL) {
375       while ((user_dirent = readdir(user_dir_stream)) != NULL) {
376          if (user_dirent->d_name[0] == '.')
377             continue;
378          *attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
379          st=get_dpi_attr(user_dpidir, user_dirent->d_name, &(*attlist)[snum]);
380          if (st == 0)
381             snum++;
382       }
383       closedir(user_dir_stream);
384    }
385    if (sys_dpidir && (sys_dir_stream = opendir(sys_dpidir)) != NULL) {
386       /* if system service is not in user list then add it */
387       while ((sys_dirent = readdir(sys_dir_stream)) != NULL) {
388          if (sys_dirent->d_name[0] == '.')
389            continue;
390          *attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
391          st=get_dpi_attr(sys_dpidir, sys_dirent->d_name, &(*attlist)[snum]);
392          if (st == 0)
393             snum++;
394       }
395       closedir(sys_dir_stream);
396    }
397 
398    dFree(sys_dpidir);
399    dFree(user_dpidir);
400 
401    /* TODO: do we consider snum == 0 an error?
402     *       (if so, we should return -1 )       */
403    return (snum);
404 }
405 
406 /*
407  * Compare two struct service pointers
408  * This function is used for sorting services
409  */
services_alpha_comp(const struct service * s1,const struct service * s2)410 static int services_alpha_comp(const struct service *s1,
411                                const struct service *s2)
412 {
413    return -strcmp(s1->name, s2->name);
414 }
415 
416 /*! Add services reading a dpidrc file
417  * each non empty or commented line has the form
418  * service = path_relative_to_dpidir
419  * \Return:
420  * \li Returns number of available services on success
421  * \li -1 on failure
422  */
fill_services_list(struct dp * attlist,int numdpis,Dlist ** services_list)423 int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list)
424 {
425    FILE *dpidrc_stream;
426    char *p, *line = NULL, *service, *path;
427    int i, st;
428    struct service *s;
429    char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
430 
431    user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
432    if (access(user_dpidir, F_OK) == -1) {
433       /* no dpis in user's space */
434       dFree(user_dpidir);
435       user_dpidir = NULL;
436    }
437    dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
438    if (access(dpidrc, F_OK) == -1) {
439       dFree(dpidrc);
440       dpidrc = dStrdup(DPIDRC_SYS);
441       if (access(dpidrc, F_OK) == -1) {
442          dFree(dpidrc);
443          dpidrc = NULL;
444       }
445    }
446    if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
447       sys_dpidir = NULL;
448 
449    if (!user_dpidir && !sys_dpidir) {
450       ERRMSG("fill_services_list", "Fatal error ", 0);
451       MSG_ERR("\n - Can't find the directory for dpis.\n");
452       exit(1);
453    }
454 
455    if ((dpidrc_stream = fopen(dpidrc, "r")) == NULL) {
456       ERRMSG("fill_services_list", "popen failed", errno);
457       dFree(dpidrc);
458       dFree(sys_dpidir);
459       dFree(user_dpidir);
460       return (-1);
461    }
462 
463    if (*services_list != NULL) {
464       ERRMSG("fill_services_list", "services_list parameter is not NULL", 0);
465       return -1;
466    }
467    *services_list = dList_new(8);
468 
469    /* dpidrc parser loop */
470    for (;(line = dGetline(dpidrc_stream)) != NULL; dFree(line)) {
471       st = dParser_parse_rc_line(&line, &service, &path);
472       if (st < 0) {
473          MSG_ERR("dpid: Syntax error in %s: service=\"%s\" path=\"%s\"\n",
474                  dpidrc, service, path);
475          continue;
476       } else if (st != 0) {
477          continue;
478       }
479 
480       _MSG("dpid: service=%s, path=%s\n", service, path);
481 
482       /* ignore dpi_dir silently */
483       if (strcmp(service, "dpi_dir") == 0)
484          continue;
485 
486       s = dNew(struct service, 1);
487       /* init services list entry */
488       s->name = dStrdup(service);
489       s->dp_index = -1;
490 
491       dList_append(*services_list, s);
492       /* search the dpi for a service by its path */
493       for (i = 0; i < numdpis; i++)
494           if ((p = strstr(attlist[i].path, path)) && *(p - 1) == '/' &&
495               strlen(p) == strlen(path))
496              break;
497       /* if the dpi exist bind service and dpi */
498       if (i < numdpis)
499          s->dp_index = i;
500    }
501    fclose(dpidrc_stream);
502 
503    dList_sort(*services_list, (dCompareFunc)services_alpha_comp);
504 
505    dFree(dpidrc);
506    dFree(sys_dpidir);
507    dFree(user_dpidir);
508 
509    return (dList_length(*services_list));
510 }
511 
512 /*
513  * Return a socket file descriptor
514  * (useful to set socket options in a uniform way)
515  */
make_socket_fd()516 static int make_socket_fd()
517 {
518    int ret, one = 1;
519 
520    if ((ret = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
521       ERRMSG("make_socket_fd", "socket", errno);
522    } else {
523       /* avoid delays when sending small pieces of data */
524       setsockopt(ret, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
525    }
526 
527    /* set some buffering to increase the transfer's speed */
528    //setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF,
529    //           &sock_buflen, (socklen_t)sizeof(sock_buflen));
530 
531    return ret;
532 }
533 
534 /*! Bind a socket port on localhost. Try to be close to base_port.
535  * \Return
536  * \li listening socket file descriptor on success
537  * \li -1 on failure
538  */
bind_socket_fd(int base_port,int * p_port)539 int bind_socket_fd(int base_port, int *p_port)
540 {
541    int sock_fd, port;
542    struct sockaddr_in sin;
543    int ok = 0, last_port = base_port + 50;
544 
545    if ((sock_fd = make_socket_fd()) == -1) {
546       return (-1);              /* avoids nested ifs */
547    }
548    /* Set the socket FD to close on exec */
549    fcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD));
550 
551 
552    memset(&sin, 0, sizeof(sin));
553    sin.sin_family = AF_INET;
554    sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
555 
556    /* Try to bind a port on localhost */
557    for (port = base_port; port <= last_port; ++port) {
558       sin.sin_port = htons(port);
559       if ((bind(sock_fd, (struct sockaddr *)&sin, sizeof(sin))) == -1) {
560          if (errno == EADDRINUSE || errno == EADDRNOTAVAIL)
561             continue;
562          ERRMSG("bind_socket_fd", "bind", errno);
563       } else if (listen(sock_fd, QUEUE) == -1) {
564          ERRMSG("bind_socket_fd", "listen", errno);
565       } else {
566          *p_port = port;
567          ok = 1;
568          break;
569       }
570    }
571    if (port > last_port) {
572       MSG_ERR("Hey! Can't find an available port from %d to %d\n",
573               base_port, last_port);
574    }
575 
576    return ok ? sock_fd : -1;
577 }
578 
579 /*! Save the current port and a shared secret in a file so dillo can find it.
580  * \Return:
581  * \li -1 on failure
582  */
save_comm_keys(int srs_port)583 int save_comm_keys(int srs_port)
584 {
585    int fd, ret = -1;
586    char *fname, port_str[32];
587 
588    fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
589    fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
590    dFree(fname);
591    if (fd == -1) {
592       MSG("save_comm_keys: open %s\n", dStrerror(errno));
593    } else {
594       snprintf(port_str, 16, "%d %s\n", srs_port, SharedKey);
595       if (CKD_WRITE(fd, port_str) != -1 && CKD_CLOSE(fd) != -1) {
596          ret = 1;
597       }
598    }
599 
600    return ret;
601 }
602 
603 /*! Initialise the service request socket (IDS)
604  * \Return:
605  * \li Number of sockets (1 == success)
606  * \li -1 on failure
607  */
init_ids_srs_socket()608 int init_ids_srs_socket()
609 {
610    int srs_port, ret = -1;
611 
612    FD_ZERO(&sock_set);
613 
614    if ((srs_fd = bind_socket_fd(DPID_BASE_PORT, &srs_port)) != -1) {
615       /* create the shared secret */
616       SharedKey = a_Misc_mksecret(8);
617       /* save port number and SharedKey */
618       if (save_comm_keys(srs_port) != -1) {
619          FD_SET(srs_fd, &sock_set);
620          ret = 1;
621       }
622    }
623 
624    return ret;
625 }
626 
627 /*! Initialize a single dpi socket
628  * \Return
629  * \li 1 on success
630  * \li -1 on failure
631  */
init_dpi_socket(struct dp * dpi_attr)632 int init_dpi_socket(struct dp *dpi_attr)
633 {
634    int s_fd, port, ret = -1;
635 
636    if ((s_fd = bind_socket_fd(DPID_BASE_PORT, &port)) != -1) {
637       dpi_attr->sock_fd = s_fd;
638       dpi_attr->port = port;
639       FD_SET(s_fd, &sock_set);
640       ret = 1;
641    }
642 
643    return ret;
644 }
645 
646 /*! Setup sockets for the plugins and add them to
647  * the set of sockets (sock_set) watched by select.
648  * \Return
649  * \li Number of sockets on success
650  * \li -1 on failure
651  * \Modifies
652  * dpi_attr_list.sa, dpi_attr_list.socket, numsocks, sock_set, srs
653  * \Uses
654  * numdpis, srs, srs_name
655  */
init_all_dpi_sockets(struct dp * dpi_attr_list)656 int init_all_dpi_sockets(struct dp *dpi_attr_list)
657 {
658    int i;
659 
660    /* Initialise sockets for each dpi */
661    for (i = 0; i < numdpis; i++) {
662       if (init_dpi_socket(dpi_attr_list + i) == -1)
663          return (-1);
664       numsocks++;
665    }
666 
667    return (numsocks);
668 }
669 
670 /*! SIGCHLD handler
671  */
dpi_sigchld(int sig)672 void dpi_sigchld(int sig)
673 {
674    if (sig == SIGCHLD)
675       caught_sigchld = 1;
676 }
677 
678 /*! Called by main loop when caught_sigchld == 1 */
handle_sigchld(void)679 void handle_sigchld(void)
680 {
681    // pid_t pid;
682    int i, status; //, num_active;
683 
684    /* For all of the dpis in the current list
685     *    add the ones that have exited to the set of sockets being
686     *    watched by 'select'.
687     */
688    for (i = 0; i < numdpis; i++) {
689       if (waitpid(dpi_attr_list[i].pid, &status, WNOHANG) > 0) {
690          dpi_attr_list[i].pid = 1;
691          FD_SET(dpi_attr_list[i].sock_fd, &sock_set);
692          numsocks++;
693       }
694    }
695 
696    /* Wait for any old dpis that have exited */
697    while (waitpid(-1, &status, WNOHANG) > 0)
698       ;
699 }
700 
701 /*! Establish SIGCHLD handler */
est_dpi_sigchld(void)702 void est_dpi_sigchld(void)
703 {
704    struct sigaction sigact;
705    sigset_t set;
706 
707    (void) sigemptyset(&set);
708    sigact.sa_handler = dpi_sigchld;
709    sigact.sa_mask = set;
710    sigact.sa_flags = SA_NOCLDSTOP;
711    if (sigaction(SIGCHLD, &sigact, NULL) == -1) {
712       ERRMSG("est_dpi_sigchld", "sigaction", errno);
713       exit(1);
714    }
715 }
716 
717 /*! EINTR aware connect() call */
ckd_connect(int sock_fd,struct sockaddr * addr,socklen_t len)718 int ckd_connect (int sock_fd, struct sockaddr *addr, socklen_t len)
719 {
720    ssize_t ret;
721 
722    do {
723       ret = connect(sock_fd, addr, len);
724    } while (ret == -1 && errno == EINTR);
725    if (ret == -1) {
726       ERRMSG("dpid.c", "connect", errno);
727    }
728    return ret;
729 }
730 
731 /*! Send DpiBye command to all active non-filter dpis
732  */
stop_active_dpis(struct dp * dpi_attr_list,int numdpis)733 void stop_active_dpis(struct dp *dpi_attr_list, int numdpis)
734 {
735    char *bye_cmd, *auth_cmd;
736    int i, sock_fd;
737    struct sockaddr_in sin;
738 
739    bye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
740    auth_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey);
741 
742    memset(&sin, 0, sizeof(sin));
743    sin.sin_family = AF_INET;
744    sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
745 
746    for (i = 0; i < numdpis; i++) {
747       /* Skip inactive dpis and filters */
748       if (dpi_attr_list[i].pid == 1 || dpi_attr_list[i].filter)
749          continue;
750 
751       if ((sock_fd = make_socket_fd()) == -1) {
752          ERRMSG("stop_active_dpis", "socket", errno);
753          continue;
754       }
755 
756       sin.sin_port = htons(dpi_attr_list[i].port);
757       if (ckd_connect(sock_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
758          ERRMSG("stop_active_dpis", "connect", errno);
759          MSG_ERR("%s\n", dpi_attr_list[i].path);
760       } else if (CKD_WRITE(sock_fd, auth_cmd) == -1) {
761          ERRMSG("stop_active_dpis", "write", errno);
762       } else if (CKD_WRITE(sock_fd, bye_cmd) == -1) {
763          ERRMSG("stop_active_dpis", "write", errno);
764       }
765       dClose(sock_fd);
766    }
767 
768    dFree(auth_cmd);
769    dFree(bye_cmd);
770 
771    /* Allow child dpis some time to read dpid_comm_keys before erasing it */
772    sleep (1);
773 }
774 
775 /*! Removes dpis in dpi_attr_list from the
776  * set of sockets watched by select and
777  * closes their sockets.
778  */
ignore_dpi_sockets(struct dp * dpi_attr_list,int numdpis)779 void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
780 {
781    int i;
782 
783    for (i = 0; i < numdpis; i++) {
784       FD_CLR(dpi_attr_list[i].sock_fd, &sock_set);
785       dClose(dpi_attr_list[i].sock_fd);
786    }
787 }
788 
789 /*! Registers available dpis and stops active non-filter dpis.
790  * Called when dpid receives
791  * cmd='register' service='all'
792  * command
793  * \Return
794  * Number of available dpis
795  */
register_all_cmd()796 int register_all_cmd()
797 {
798    stop_active_dpis(dpi_attr_list, numdpis);
799    free_plugin_list(&dpi_attr_list, numdpis);
800    free_services_list(services_list);
801    services_list = NULL;
802    numdpis = 0;
803    numsocks = 1;                /* the srs socket */
804    FD_ZERO(&sock_set);
805    FD_SET(srs_fd, &sock_set);
806    numdpis = register_all(&dpi_attr_list);
807    fill_services_list(dpi_attr_list, numdpis, &services_list);
808    numsocks = init_all_dpi_sockets(dpi_attr_list);
809    return (numdpis);
810 }
811 
812 /*!
813  * Get value of msg field from dpi_tag
814  * \Return
815  * message on success, NULL on failure
816  */
get_message(int sock_fd,char * dpi_tag)817 char *get_message(int sock_fd, char *dpi_tag)
818 {
819    char *msg, *d_cmd;
820 
821    msg = a_Dpip_get_attr(dpi_tag, "msg");
822    if (msg == NULL) {
823       ERRMSG("get_message", "failed to parse msg", 0);
824       d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
825                                "DpiError", "Failed to parse request");
826       (void) CKD_WRITE(sock_fd, d_cmd);
827       dFree(d_cmd);
828    }
829    return (msg);
830 }
831 
832 /*
833  * Compare a struct service pointer and a service name
834  * This function is used for searching services by name
835  */
service_match(const struct service * A,const char * B)836 int service_match(const struct service *A, const char *B)
837 {
838    int A_len, B_len, len;
839 
840    A_len = strlen(A->name);
841    B_len = strlen(B);
842    len = MAX (A_len, B_len);
843 
844    if (A->name[A_len - 1] == '*')
845       len = A_len - 1;
846 
847    return(dStrnAsciiCasecmp(A->name, B, len));
848 }
849 
850 /*!
851  * Send socket port that matches dpi_id to client
852  */
send_sockport(int sock_fd,char * dpi_tag,struct dp * dpi_attr_list)853 void send_sockport(int sock_fd, char *dpi_tag, struct dp *dpi_attr_list)
854 {
855    int i;
856    char *dpi_id, *d_cmd, port_str[16];
857    struct service *serv;
858 
859    dReturn_if_fail((dpi_id = get_message(sock_fd, dpi_tag)) != NULL);
860 
861    serv = dList_find_custom(services_list,dpi_id,(dCompareFunc)service_match);
862 
863    if (serv == NULL || (i = serv->dp_index) == -1)
864       for (i = 0; i < numdpis; i++)
865          if (!strncmp(dpi_attr_list[i].id, dpi_id,
866                       dpi_attr_list[i].id - strchr(dpi_attr_list[i].id, '.')))
867             break;
868 
869    if (i < numdpis) {
870       /* found */
871       snprintf(port_str, 8, "%d", dpi_attr_list[i].port);
872       d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_data", port_str);
873       (void) CKD_WRITE(sock_fd, d_cmd);
874       dFree(d_cmd);
875    }
876 
877    dFree(dpi_id);
878 }
879