1 /*****************************************************************************
2  *  Written by Chris Dunlap <cdunlap@llnl.gov>.
3  *  Copyright (C) 2007-2018 Lawrence Livermore National Security, LLC.
4  *  Copyright (C) 2001-2007 The Regents of the University of California.
5  *  UCRL-CODE-2002-009.
6  *
7  *  This file is part of ConMan: The Console Manager.
8  *  For details, see <https://dun.github.io/conman/>.
9  *
10  *  ConMan is free software: you can redistribute it and/or modify it under
11  *  the terms of the GNU General Public License as published by the Free
12  *  Software Foundation, either version 3 of the License, or (at your option)
13  *  any later version.
14  *
15  *  ConMan is distributed in the hope that it will be useful, but WITHOUT
16  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18  *  for more details.
19  *
20  *  You should have received a copy of the GNU General Public License along
21  *  with ConMan.  If not, see <http://www.gnu.org/licenses/>.
22  *****************************************************************************/
23 
24 
25 #if HAVE_CONFIG_H
26 #  include <config.h>
27 #endif /* HAVE_CONFIG_H */
28 
29 #include <assert.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #include <time.h>
39 #include <unistd.h>
40 #include "list.h"
41 #include "log.h"
42 #include "server.h"
43 #include "tpoll.h"
44 #include "util.h"
45 #include "util-file.h"
46 #include "util-str.h"
47 
48 
49 static int search_exec_path(const char *path, const char *src,
50     char *dst, int dstlen);
51 static int  disconnect_process_obj(obj_t *process);
52 static int  connect_process_obj(obj_t *process);
53 static int  check_process_prog(obj_t *process);
54 static void reset_process_delay(obj_t *process);
55 
56 extern tpoll_t tp_global;               /* defined in server.c */
57 
58 
is_process_dev(const char * dev,const char * cwd,const char * exec_path,char ** path_ref)59 int is_process_dev(const char *dev, const char *cwd,
60     const char *exec_path, char **path_ref)
61 {
62     char         buf[PATH_MAX];
63     int          n;
64     struct stat  st;
65 
66     assert(dev != NULL);
67 
68     if (!strchr(dev, '/')
69             && (search_exec_path(exec_path, dev, buf, sizeof(buf)) == 0)) {
70         dev = buf;
71     }
72     else if ((dev[0] != '/') && (cwd != NULL)) {
73         n = snprintf(buf, sizeof(buf), "%s/%s", cwd, dev);
74         if ((n < 0) || ((size_t) n >= sizeof(buf))) {
75             return(0);
76         }
77         dev = buf;
78     }
79     if (stat(dev, &st) < 0) {
80         return(0);
81     }
82     if (!S_ISREG(st.st_mode)) {
83         return(0);
84     }
85     if (access(dev, X_OK) < 0) {
86         return(0);
87     }
88     if (path_ref) {
89         *path_ref = create_string(dev);
90     }
91     return(1);
92 }
93 
94 
search_exec_path(const char * path,const char * src,char * dst,int dstlen)95 static int search_exec_path(const char *path, const char *src,
96     char *dst, int dstlen)
97 {
98     char         path_buf[PATH_MAX];
99     char         file_buf[PATH_MAX];
100     char        *p;
101     char        *q;
102     struct stat  st;
103     int          n;
104 
105     assert((path == NULL) || (strlen(path) < PATH_MAX));
106     assert(src != NULL);
107     assert(strchr(src, '/') == NULL);
108     assert(dst != NULL);
109     assert(dstlen >= 0);
110 
111     if (!path) {
112         return(-1);
113     }
114     if (strlcpy(path_buf, path, sizeof(path_buf)) >= sizeof(path_buf)) {
115         return(-1);
116     }
117     for (p = path_buf; p && *p; p = q) {
118         if ((q = strchr(p, ':'))) {
119             *q++ = '\0';
120         }
121         else {
122             q = strchr(p, '\0');
123         }
124         if (stat(p, &st) < 0) {
125             continue;
126         }
127         if (!S_ISDIR(st.st_mode)) {
128             continue;
129         }
130         n = snprintf(file_buf, sizeof(file_buf), "%s/%s", p, src);
131         if ((n < 0) || ((size_t) n >= sizeof(file_buf))) {
132             continue;
133         }
134         if (access(file_buf, X_OK) < 0) {
135             continue;
136         }
137         if ((dst != NULL) && (dstlen > 0)) {
138             if (strlcpy(dst, file_buf, dstlen) >= (size_t) dstlen) {
139                 return(1);
140             }
141         }
142         return(0);
143     }
144     return(-1);
145 }
146 
147 
create_process_obj(server_conf_t * conf,char * name,List args,char * errbuf,int errlen)148 obj_t * create_process_obj(server_conf_t *conf, char *name, List args,
149     char *errbuf, int errlen)
150 {
151 /*  Creates a new process device object and adds it to the master objs list.
152  *  Note: an external process will later be fork/exec'd based on the argv
153  *    by main:open_objs:reopen_obj:open_process_obj().
154  *  Returns the new object, or NULL on error.
155  */
156     ListIterator   i;
157     obj_t         *process;
158     process_obj_t *auxp;
159     int            num_args;
160     int            n;
161     char          *arg;
162 
163     assert(conf != NULL);
164     assert((name != NULL) && (name[0] != '\0'));
165     assert((args != NULL) && (list_count(args) > 0));
166 
167     /*  Check for duplicate console names.
168      */
169     i = list_iterator_create(conf->objs);
170     while ((process = list_next(i))) {
171         if (is_console_obj(process) && !strcmp(process->name, name)) {
172             if ((errbuf != NULL) && (errlen > 0)) {
173                 snprintf(errbuf, errlen,
174                     "console [%s] specifies duplicate console name", name);
175             }
176             break;
177         }
178     }
179     list_iterator_destroy(i);
180     if (process != NULL) {
181         return(NULL);
182     }
183     process = create_obj(conf, name, -1, CONMAN_OBJ_PROCESS);
184     auxp = &(process->aux.process);
185 
186     auxp->timer = -1;
187     auxp->delay = PROCESS_MIN_TIMEOUT;
188     auxp->pid = -1;
189     auxp->tStart = 0;
190     auxp->logfile = NULL;
191     auxp->state = CONMAN_PROCESS_DOWN;
192     num_args = list_count(args);
193     auxp->argv = calloc(num_args + 1, sizeof(char *));
194     if (!auxp->argv) {
195         out_of_memory();
196     }
197     for (n = 0; (n < num_args) && (arg = list_pop(args)); n++) {
198         auxp->argv[n] = arg;
199     }
200     auxp->argv[n] = (char *) NULL;
201     if ((arg = strrchr(auxp->argv[0], '/'))) {
202         auxp->prog = arg + 1;
203     }
204     else {
205         auxp->prog = auxp->argv[0];
206     }
207     /*  Add obj to the master conf->objs list.
208      */
209     list_append(conf->objs, process);
210 
211     return(process);
212 }
213 
214 
open_process_obj(obj_t * process)215 int open_process_obj(obj_t *process)
216 {
217 /*  (Re)opens the specified 'process' obj.
218  *  Returns 0 if the child process is successfully opened;
219  *    o/w, sets a reconnect timer and returns -1.
220  */
221     process_obj_t *auxp;
222     int            rc = 0;
223 
224     assert(process != NULL);
225     assert(is_process_obj(process));
226 
227     auxp = &(process->aux.process);
228 
229     if (auxp->timer >= 0) {
230         (void) tpoll_timeout_cancel(tp_global, auxp->timer);
231         auxp->timer = -1;
232     }
233 
234     if (auxp->state == CONMAN_PROCESS_UP) {
235         rc = disconnect_process_obj(process);
236     }
237     else if (auxp->state == CONMAN_PROCESS_DOWN) {
238         rc = connect_process_obj(process);
239     }
240 
241     if (rc < 0) {
242         DPRINTF((15, "Retrying [%s] connection to prog=\"%s\" in %ds\n",
243             process->name, auxp->argv[0], auxp->delay));
244 
245         auxp->timer = tpoll_timeout_relative(tp_global,
246             (callback_f) open_process_obj, process, auxp->delay * 1000);
247 
248         auxp->delay = (auxp->delay == 0)
249             ? PROCESS_MIN_TIMEOUT
250             : MIN(auxp->delay * 2, PROCESS_MAX_TIMEOUT);
251     }
252     return(rc);
253 }
254 
255 
disconnect_process_obj(obj_t * process)256 static int disconnect_process_obj(obj_t *process)
257 {
258 /*  Closes the existing connection with the specified 'process' obj.
259  *  Always returns -1 to signal a reconnect.
260  */
261     process_obj_t *auxp;
262     time_t         tNow;
263     char          *delta_str;
264 
265     assert(process != NULL);
266     assert(process->aux.process.pid > 0);
267     assert(process->aux.process.tStart > 0);
268     assert(process->aux.process.state != CONMAN_PROCESS_DOWN);
269 
270     auxp = &(process->aux.process);
271 
272     if (process->fd >= 0) {
273         tpoll_clear(tp_global, process->fd, POLLIN | POLLOUT);
274         (void) close(process->fd);
275         process->fd = -1;
276     }
277     if (time(&tNow) == (time_t) -1) {
278         log_err(errno, "time() failed");
279     }
280     delta_str = create_time_delta_string(auxp->tStart, tNow);
281 
282     /*  Notify linked objs when transitioning from an UP state.
283      */
284     assert(auxp->state == CONMAN_PROCESS_UP);
285     write_notify_msg(process, LOG_INFO,
286         "Console [%s] disconnected from \"%s\" (pid %d) after %s",
287         process->name, auxp->prog, auxp->pid, delta_str);
288     free(delta_str);
289 
290     (void) kill(auxp->pid, SIGKILL);
291     auxp->pid = -1;
292     auxp->tStart = 0;
293     auxp->state = CONMAN_PROCESS_DOWN;
294     return (-1);
295 }
296 
297 
connect_process_obj(obj_t * process)298 static int connect_process_obj(obj_t *process)
299 {
300 /*  Opens a connection to the specified 'process' obj.
301  *  Returns 0 if the connection is successfully completed; o/w, returns -1.
302  */
303     process_obj_t *auxp;
304     int            fd_pair[2] = {-1,-1};
305     pid_t          pid;
306 
307     assert(process != NULL);
308     assert(process->fd == -1);
309     assert(process->aux.process.pid == -1);
310     assert(process->aux.process.state != CONMAN_PROCESS_UP);
311 
312     auxp = &(process->aux.process);
313 
314     if (check_process_prog(process) < 0) {
315         goto err;
316     }
317     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd_pair) < 0) {
318         write_notify_msg(process, LOG_WARNING,
319             "Console [%s] connection failed: socketpair error: %s",
320             process->name, strerror(errno));
321         goto err;
322     }
323     set_fd_nonblocking(fd_pair[0]);
324     set_fd_nonblocking(fd_pair[1]);
325     set_fd_closed_on_exec(fd_pair[0]);
326     set_fd_closed_on_exec(fd_pair[1]);
327 
328     if ((pid = fork()) < 0) {
329         write_notify_msg(process, LOG_WARNING,
330             "Console [%s] connection failed: fork error: %s",
331             process->name, strerror(errno));
332         goto err;
333     }
334     else if (pid == 0) {
335         if (close(fd_pair[0]) < 0) {
336             log_err(errno, "close() of child fd_pair failed");
337         }
338         if (dup2(fd_pair[1], STDIN_FILENO) < 0) {
339             log_err(errno, "dup2() of child stdin failed");
340         }
341         if (dup2(fd_pair[1], STDOUT_FILENO) < 0) {
342             log_err(errno, "dup2() of child stdout failed");
343         }
344         if (dup2(fd_pair[1], STDERR_FILENO) < 0) {
345             log_err(errno, "dup2() of child stderr failed");
346         }
347         execv(auxp->argv[0], auxp->argv);
348         _exit(127);
349     }
350     if (close(fd_pair[1]) < 0) {
351         log_err(errno, "close() of parent fd_pair failed");
352     }
353     if (time(&(auxp->tStart)) == (time_t) -1) {
354         log_err(errno, "time() failed");
355     }
356     process->fd = fd_pair[0];
357     auxp->pid = pid;
358     process->gotEOF = 0;
359     auxp->state = CONMAN_PROCESS_UP;
360     tpoll_set(tp_global, process->fd, POLLIN);
361 
362     /*  Require the connection to be up for a minimum length of time before
363      *    resetting the reconnect-delay back to zero.
364      */
365     auxp->timer = tpoll_timeout_relative(tp_global,
366         (callback_f) reset_process_delay, process, PROCESS_MIN_TIMEOUT * 1000);
367 
368     /*  Notify linked objs when transitioning into an UP state.
369      */
370     write_notify_msg(process, LOG_INFO,
371         "Console [%s] connected to \"%s\" (pid %d)",
372         process->name, auxp->prog, auxp->pid);
373     DPRINTF((9, "Opened [%s] process: fd=%d/%d prog=\"%s\" pid=%d.\n",
374         process->name, fd_pair[0], fd_pair[1], auxp->argv[0], auxp->pid));
375 
376     return(0);
377 
378 err:
379     if (fd_pair[0] >= 0) {
380         (void) close(fd_pair[0]);
381     }
382     if (fd_pair[1] >= 0) {
383         (void) close(fd_pair[1]);
384     }
385     return(-1);
386 }
387 
388 
check_process_prog(obj_t * process)389 static int check_process_prog(obj_t *process)
390 {
391 /*  Checks whether the 'process' executable will likely exec.
392  *  Returns 0 if all checks pass; o/w, returns -1.
393  */
394     process_obj_t *auxp;
395     struct stat    st;
396 
397     assert(process != NULL);
398 
399     auxp = &(process->aux.process);
400 
401     if (stat(auxp->argv[0], &st) < 0) {
402         write_notify_msg(process, LOG_WARNING,
403             "Console [%s] connection failed: \"%s\" stat error: %s",
404             process->name, auxp->prog, strerror(errno));
405         return(-1);
406     }
407     if (!S_ISREG(st.st_mode)) {
408         write_notify_msg(process, LOG_WARNING,
409             "Console [%s] connection failed: \"%s\" not a regular file",
410             process->name, auxp->prog);
411         return(-1);
412     }
413     if (access(auxp->argv[0], X_OK) < 0) {
414         write_notify_msg(process, LOG_WARNING,
415             "Console [%s] connection failed: \"%s\" not executable",
416             process->name, auxp->prog);
417         return(-1);
418     }
419     return(0);
420 }
421 
422 
reset_process_delay(obj_t * process)423 static void reset_process_delay(obj_t *process)
424 {
425 /*  Resets the process obj's reconnect-delay after the connection has been up
426  *    for the minimum length of time.  This protects against spinning on
427  *    reconnects where the process immediately terminates.
428  */
429     process_obj_t *auxp;
430 
431     assert(process != NULL);
432     assert(is_process_obj(process));
433 
434     auxp = &(process->aux.process);
435 
436     /*  Reset the timer ID since this routine is only invoked by a timer.
437      */
438     auxp->timer = -1;
439 
440     DPRINTF((15, "Reset [%s] reconnect delay\n", process->name));
441     auxp->delay = 0;
442     return;
443 }
444