fix conf_set()
[ladish.git] / daemon / loader.c
blob0a0f288b9357a7d4f6b5e3f6231167f1bd3d0cd4
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008, 2009, 2010 Nedko Arnaudov <nedko@arnaudov.name>
6 * Copyright (C) 2008 Juuso Alasuutari <juuso.alasuutari@gmail.com>
7 * Copyright (C) 2002 Robert Ham <rah@bash.sh>
9 **************************************************************************
10 * This file contains code that starts programs
11 **************************************************************************
13 * LADI Session Handler is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * LADI Session Handler is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
25 * or write to the Free Software Foundation, Inc.,
26 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 #define LADISH_DEBUG
31 #include "common.h"
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <pty.h> /* forkpty() */
36 #include <sys/wait.h>
38 #include "loader.h"
39 #include "../proxies/conf_proxy.h"
40 #include "conf.h"
42 #define XTERM_COMMAND_EXTENSION "&& sh || sh"
44 #define CLIENT_OUTPUT_BUFFER_SIZE 2048
46 struct loader_child
48 struct list_head siblings;
50 char * project_name;
51 char * app_name;
53 bool dead;
54 pid_t pid;
56 bool terminal;
58 int stdout;
59 char stdout_buffer[CLIENT_OUTPUT_BUFFER_SIZE];
60 char stdout_last_line[CLIENT_OUTPUT_BUFFER_SIZE];
61 unsigned int stdout_last_line_repeat_count;
62 char * stdout_buffer_ptr;
64 int stderr;
65 char stderr_buffer[CLIENT_OUTPUT_BUFFER_SIZE];
66 char stderr_last_line[CLIENT_OUTPUT_BUFFER_SIZE];
67 unsigned int stderr_last_line_repeat_count;
68 char * stderr_buffer_ptr;
71 static void (* g_on_child_exit)(pid_t pid);
72 static struct list_head g_childs_list;
74 static struct loader_child *
75 loader_child_find_and_mark_dead(pid_t pid)
77 struct list_head *node_ptr;
78 struct loader_child *child_ptr;
80 list_for_each (node_ptr, &g_childs_list)
82 child_ptr = list_entry(node_ptr, struct loader_child, siblings);
83 if (child_ptr->pid == pid)
85 child_ptr->dead = true;
86 return child_ptr;
90 return NULL;
93 static
94 void
95 loader_check_line_repeat_end(
96 char * project,
97 char * app_name,
98 bool error,
99 unsigned int last_line_repeat_count)
101 if (last_line_repeat_count >= 2)
103 if (error)
105 log_error_plain("%s:%s: stderr line repeated %u times", project, app_name, last_line_repeat_count);
107 else
109 log_info("%s:%s: stdout line repeated %u times", project, app_name, last_line_repeat_count);
114 static void
115 loader_childs_bury(void)
117 struct list_head *node_ptr;
118 struct list_head *next_ptr;
119 struct loader_child *child_ptr;
121 list_for_each_safe (node_ptr, next_ptr, &g_childs_list)
123 child_ptr = list_entry(node_ptr, struct loader_child, siblings);
124 if (child_ptr->dead)
126 loader_check_line_repeat_end(
127 child_ptr->project_name,
128 child_ptr->app_name,
129 false,
130 child_ptr->stdout_last_line_repeat_count);
132 loader_check_line_repeat_end(
133 child_ptr->project_name,
134 child_ptr->app_name,
135 true,
136 child_ptr->stderr_last_line_repeat_count);
138 log_debug("Bury child '%s' with PID %llu", child_ptr->app_name, (unsigned long long)child_ptr->pid);
140 list_del(&child_ptr->siblings);
142 free(child_ptr->project_name);
143 free(child_ptr->app_name);
145 if (!child_ptr->terminal)
147 close(child_ptr->stdout);
148 close(child_ptr->stderr);
151 g_on_child_exit(child_ptr->pid);
152 free(child_ptr);
157 static void loader_sigchld_handler(int signum)
159 int status;
160 pid_t pid;
161 struct loader_child *child_ptr;
162 int signal;
164 while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
166 child_ptr = loader_child_find_and_mark_dead(pid);
168 if (!child_ptr)
170 log_error("Termination of unknown child process with PID %llu detected", (unsigned long long)pid);
172 else
174 log_info("Termination of child process '%s' with PID %llu detected", child_ptr->app_name, (unsigned long long)pid);
177 if (WIFEXITED(status))
179 log_info("Child exited, status=%d", WEXITSTATUS(status));
181 else if (WIFSIGNALED(status))
183 signal = WTERMSIG(status);
184 switch (signal)
186 case SIGILL:
187 case SIGABRT:
188 case SIGSEGV:
189 case SIGFPE:
190 log_error("Child was killed by signal %d", signal);
191 break;
192 default:
193 log_info("Child was killed by signal %d", signal);
196 else if (WIFSTOPPED(status))
198 log_info("Child was stopped by signal %d", WSTOPSIG(status));
203 void loader_init(void (* on_child_exit)(pid_t pid))
205 g_on_child_exit = on_child_exit;
206 signal(SIGCHLD, loader_sigchld_handler);
207 INIT_LIST_HEAD(&g_childs_list);
210 void loader_uninit(void)
212 loader_childs_bury();
215 #if 0
216 static void loader_exec_program_in_xterm(const char * const * argv)
218 char * dst_ptr;
219 const char * const * src_ptr_ptr;
220 size_t len;
222 log_debug("Executing program '%s' with PID %llu in terminal", argv[0], (unsigned long long)getpid());
224 /* Calculate the command string length */
225 len = strlen(XTERM_COMMAND_EXTENSION) + 1;
226 for (src_ptr_ptr = argv; *src_ptr_ptr != NULL; src_ptr_ptr++)
228 len += strlen(*src_ptr_ptr) + 3; /* three additional chars per argument: two double quotes and a space */
231 char buf[len]; /* dynamically allocate in stack */
233 /* Create the command string */
234 src_ptr_ptr = argv;
235 dst_ptr = buf;
236 while (*src_ptr_ptr != NULL)
238 len = strlen(*src_ptr_ptr);
239 dst_ptr[0] = '"';
240 memcpy(dst_ptr + 1, src_ptr_ptr, len);
241 dst_ptr[1 + len] = '"';
242 dst_ptr[1 + len + 1] = ' ';
243 dst_ptr += len + 3;
244 src_ptr_ptr++;
247 strcat(dst_ptr, XTERM_COMMAND_EXTENSION);
249 /* Execute the command */
250 execlp("xterm", "xterm", "-e", "/bin/sh", "-c", buf, NULL);
252 log_error("Failed to execute command '%s' in terminal: %s", buf, strerror(errno));
254 exit(1);
256 #endif
258 static void loader_exec_program(const char * commandline, const char * working_dir, bool run_in_terminal)
260 const char * argv[4];
262 /* for non terminal processes we use forkpty() that calls login_tty() that calls setsid() */
263 /* we can successful call setsid() only once */
264 if (run_in_terminal)
266 /* no longer anything to do with lashd */
267 if (setsid() == -1)
269 log_error("Could not create new process group: %s", strerror(errno));
273 /* change the working dir */
274 if (chdir(working_dir) == -1)
276 log_error("Could not change directory to working dir '%s' for program '%s': %s", working_dir, argv[0], strerror(errno));
279 if (run_in_terminal)
281 if (!conf_get(LADISH_CONF_KEY_DAEMON_TERMINAL, argv))
283 argv[0] = "xterm";
286 argv[1] = "-e";
288 else
290 if (!conf_get(LADISH_CONF_KEY_DAEMON_SHELL, argv))
292 argv[0] = "sh";
295 argv[1] = "-c";
298 argv[2] = commandline;
299 argv[3] = NULL;
301 log_info("Executing '%s' with PID %llu", commandline, (unsigned long long)getpid());
303 /* Execute it */
304 execvp(argv[0], (char **)argv);
306 log_error("Executing program '%s' failed: %s", argv[0], strerror(errno));
308 exit(1);
311 static
312 void
313 loader_read_child_output(
314 char * project,
315 char * app_name,
316 int fd,
317 bool error,
318 char * buffer_ptr,
319 char ** buffer_ptr_ptr,
320 char * last_line,
321 unsigned int * last_line_repeat_count)
323 ssize_t ret;
324 char *char_ptr;
325 char *eol_ptr;
326 size_t left;
327 size_t max_read;
331 max_read = CLIENT_OUTPUT_BUFFER_SIZE - 1 - (*buffer_ptr_ptr - buffer_ptr);
332 ret = read(fd, *buffer_ptr_ptr, max_read);
333 if (ret > 0)
335 (*buffer_ptr_ptr)[ret] = 0;
336 char_ptr = buffer_ptr;
338 while ((eol_ptr = strchr(char_ptr, '\n')) != NULL)
340 *eol_ptr = 0;
342 if (*last_line_repeat_count > 0 && strcmp(last_line, char_ptr) == 0)
344 if (*last_line_repeat_count == 1)
346 if (error)
348 log_error_plain("%s:%s: last stderr line repeating..", project, app_name);
350 else
352 log_info("%s:%s: last stdout line repeating...", project, app_name);
356 (*last_line_repeat_count)++;
358 else
360 loader_check_line_repeat_end(project, app_name, error, *last_line_repeat_count);
362 strcpy(last_line, char_ptr);
363 *last_line_repeat_count = 1;
365 if (error)
367 log_error_plain("%s:%s: %s", project, app_name, char_ptr);
369 else
371 log_info("%s:%s: %s", project, app_name, char_ptr);
375 char_ptr = eol_ptr + 1;
378 left = ret - (char_ptr - *buffer_ptr_ptr);
379 if (left != 0)
381 /* last line does not end with newline */
383 if (left == CLIENT_OUTPUT_BUFFER_SIZE - 1)
385 /* line is too long to fit in buffer */
386 /* print it like it is, rest (or more) of it will be logged on next interation */
388 if (error)
390 log_error_plain("%s:%s: %s " ANSI_RESET ANSI_COLOR_RED "(truncated) " ANSI_RESET, project, app_name, char_ptr);
392 else
394 log_info("%s:%s: %s " ANSI_RESET ANSI_COLOR_RED "(truncated) " ANSI_RESET, project, app_name, char_ptr);
397 left = 0;
399 else
401 memmove(buffer_ptr, char_ptr, left);
405 *buffer_ptr_ptr = buffer_ptr + left;
408 while (ret == max_read); /* if we have read everything as much as we can, then maybe there is more to read */
411 static void
412 loader_read_childs_output(void)
414 struct list_head * node_ptr;
415 struct loader_child * child_ptr;
417 list_for_each (node_ptr, &g_childs_list)
419 child_ptr = list_entry(node_ptr, struct loader_child, siblings);
421 if (!child_ptr->dead && !child_ptr->terminal)
423 loader_read_child_output(
424 child_ptr->project_name,
425 child_ptr->app_name,
426 child_ptr->stdout,
427 false,
428 child_ptr->stdout_buffer,
429 &child_ptr->stdout_buffer_ptr,
430 child_ptr->stdout_last_line,
431 &child_ptr->stdout_last_line_repeat_count);
433 loader_read_child_output(
434 child_ptr->project_name,
435 child_ptr->app_name,
436 child_ptr->stderr,
437 true,
438 child_ptr->stderr_buffer,
439 &child_ptr->stderr_buffer_ptr,
440 child_ptr->stderr_last_line,
441 &child_ptr->stderr_last_line_repeat_count);
446 void
447 loader_run(void)
449 loader_read_childs_output();
450 loader_childs_bury();
453 bool
454 loader_execute(
455 const char * project_name,
456 const char * app_name,
457 const char * working_dir,
458 bool run_in_terminal,
459 const char * commandline,
460 pid_t * pid_ptr)
462 pid_t pid;
463 struct loader_child * child_ptr;
464 int stderr_pipe[2];
466 child_ptr = malloc(sizeof(struct loader_child));
467 if (child_ptr == NULL)
469 log_error("malloc() failed to allocate struct loader_child");
470 goto fail;
473 child_ptr->project_name = strdup(project_name);
474 if (child_ptr->project_name == NULL)
476 log_error("strdup() failed to duplicate project name '%s'", project_name);
477 goto free_struct;
480 child_ptr->app_name = strdup(app_name);
481 if (child_ptr->app_name == NULL)
483 log_error("strdup() failed to duplicate app name '%s'", app_name);
484 goto free_project_name;
487 child_ptr->dead = false;
488 child_ptr->terminal = run_in_terminal;
489 child_ptr->stdout_buffer_ptr = child_ptr->stdout_buffer;
490 child_ptr->stderr_buffer_ptr = child_ptr->stderr_buffer;
491 child_ptr->stdout_last_line_repeat_count = 0;
492 child_ptr->stderr_last_line_repeat_count = 0;
494 if (!run_in_terminal)
496 if (pipe(stderr_pipe) == -1)
498 log_error("Failed to create stderr pipe");
500 else
502 child_ptr->stderr = stderr_pipe[0];
504 if (fcntl(child_ptr->stderr, F_SETFL, O_NONBLOCK) == -1)
506 log_error("Failed to set nonblocking mode on "
507 "stderr reading end: %s",
508 strerror(errno));
509 close(stderr_pipe[0]);
510 close(stderr_pipe[1]);
515 list_add_tail(&child_ptr->siblings, &g_childs_list);
517 if (!run_in_terminal)
519 /* We need pty to disable libc buffering of stdout */
520 pid = forkpty(&child_ptr->stdout, NULL, NULL, NULL);
522 else
524 pid = fork();
527 if (pid == -1)
529 log_error("Could not fork to exec program %s:%s: %s", project_name, app_name, strerror(errno));
530 list_del(&child_ptr->siblings); /* fork failed so it is not really a child process to watch for. */
531 return false;
534 if (pid == 0)
536 /* Need to close all open file descriptors except the std ones */
537 struct rlimit max_fds;
538 rlim_t fd;
540 getrlimit(RLIMIT_NOFILE, &max_fds);
542 for (fd = 3; fd < max_fds.rlim_cur; ++fd)
544 close(fd);
547 if (!run_in_terminal)
549 /* In child, close unused reading end of pipe */
550 close(stderr_pipe[0]);
552 dup2(stderr_pipe[1], fileno(stderr));
555 loader_exec_program(commandline, working_dir, run_in_terminal);
557 return false; /* We should never get here */
560 if (!run_in_terminal)
562 /* In parent, close unused writing ends of pipe */
563 close(stderr_pipe[1]);
565 if (fcntl(child_ptr->stdout, F_SETFL, O_NONBLOCK) == -1)
567 log_error("Could not set noblocking mode on stdout "
568 "- pty: %s", strerror(errno));
569 close(stderr_pipe[0]);
570 close(child_ptr->stdout);
574 log_info("Forked to run program %s:%s pid = %llu", project_name, app_name, (unsigned long long)pid);
576 *pid_ptr = child_ptr->pid = pid;
578 return true;
580 free_project_name:
581 free(child_ptr->project_name);
583 free_struct:
584 free(child_ptr);
586 fail:
587 return false;
590 unsigned int loader_get_app_count(void)
592 struct list_head * node_ptr;
593 unsigned int count;
595 count = 0;
597 list_for_each(node_ptr, &g_childs_list)
599 count++;
602 return count;