Merge branch 'stable' into 'main'
[ladish.git] / daemon / app_supervisor.c
blobac378f63ec84f880a7e05bd7131fb2c0864e11bd
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009, 2010, 2011, 2012 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains implementation of app supervisor object
9 **************************************************************************
11 * LADI Session Handler is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * LADI Session Handler is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
23 * or write to the Free Software Foundation, Inc.,
24 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "config.h" /* Get _GNU_SOURCE defenition first to have some GNU extension available */
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <signal.h>
32 #include <unistd.h>
33 #include <sys/wait.h>
35 #include "app_supervisor.h"
36 #include "../dbus_constants.h"
37 #include "loader.h"
38 #include "studio_internal.h"
39 #include "../proxies/notify_proxy.h"
40 #include "../proxies/lash_client_proxy.h"
41 #include "../common/catdup.h"
42 #include "../common/dirhelpers.h"
43 #include "jack_session.h"
45 struct ladish_app
47 struct list_head siblings;
48 uint64_t id;
49 uuid_t uuid;
50 char * name;
51 char * commandline;
52 char * js_commandline;
53 bool terminal;
54 char level[MAX_LEVEL_CHARCOUNT];
55 pid_t pid;
56 pid_t pgrp;
57 pid_t firstborn_pid;
58 pid_t firstborn_pgrp;
59 int firstborn_refcount;
60 bool zombie; /* if true, remove when stopped */
61 bool autorun;
62 unsigned int state;
63 char * dbus_name;
64 struct ladish_app_supervisor * supervisor;
67 struct ladish_app_supervisor
69 char * name;
70 char * opath;
71 char * dir;
73 char * js_dir;
74 char * js_temp_dir;
75 unsigned int pending_js_saves;
76 void * save_callback_context;
77 ladish_save_complete_callback save_callback;
79 char * project_name;
80 uint64_t version;
81 uint64_t next_id;
82 struct list_head applist;
83 void * on_app_renamed_context;
84 ladish_app_supervisor_on_app_renamed_callback on_app_renamed;
87 bool ladish_check_app_level_validity(const char * level, size_t * len_ptr)
89 size_t len;
90 len = strlen(level);
91 if (len >= MAX_LEVEL_CHARCOUNT)
93 return false;
96 if (strcmp(level, LADISH_APP_LEVEL_0) != 0 &&
97 strcmp(level, LADISH_APP_LEVEL_1) != 0 &&
98 strcmp(level, LADISH_APP_LEVEL_LASH) != 0 &&
99 strcmp(level, LADISH_APP_LEVEL_JACKSESSION) != 0)
101 return false;
104 if (len_ptr != NULL)
106 *len_ptr = len;
109 return true;
112 static int ladish_level_string_to_integer(const char * level)
114 if (strcmp(level, LADISH_APP_LEVEL_0) == 0)
116 return 0;
118 else if (strcmp(level, LADISH_APP_LEVEL_1) == 0)
120 return 1;
122 else if (strcmp(level, LADISH_APP_LEVEL_LASH) == 0 ||
123 strcmp(level, LADISH_APP_LEVEL_JACKSESSION) == 0)
125 return 2;
128 ASSERT_NO_PASS;
129 return 255;
132 bool
133 ladish_app_supervisor_create(
134 ladish_app_supervisor_handle * supervisor_handle_ptr,
135 const char * opath,
136 const char * name,
137 void * context,
138 ladish_app_supervisor_on_app_renamed_callback on_app_renamed)
140 struct ladish_app_supervisor * supervisor_ptr;
142 supervisor_ptr = malloc(sizeof(struct ladish_app_supervisor));
143 if (supervisor_ptr == NULL)
145 log_error("malloc() failed to allocate struct ladish_app_supervisor");
146 return false;
149 supervisor_ptr->opath = strdup(opath);
150 if (supervisor_ptr->opath == NULL)
152 log_error("strdup() failed for app supervisor opath");
153 free(supervisor_ptr);
154 return false;
157 supervisor_ptr->name = strdup(name);
158 if (supervisor_ptr->name == NULL)
160 log_error("strdup() failed for app supervisor name");
161 free(supervisor_ptr->opath);
162 free(supervisor_ptr);
163 return false;
166 supervisor_ptr->dir = NULL;
168 supervisor_ptr->js_temp_dir = NULL;
169 supervisor_ptr->js_dir = NULL;
170 supervisor_ptr->pending_js_saves = 0;
171 supervisor_ptr->save_callback_context = NULL;
172 supervisor_ptr->save_callback = NULL;
174 supervisor_ptr->project_name = NULL;
176 supervisor_ptr->version = 0;
177 supervisor_ptr->next_id = 1;
179 INIT_LIST_HEAD(&supervisor_ptr->applist);
181 supervisor_ptr->on_app_renamed_context = context;
182 supervisor_ptr->on_app_renamed = on_app_renamed;
184 *supervisor_handle_ptr = (ladish_app_supervisor_handle)supervisor_ptr;
186 return true;
189 struct ladish_app * ladish_app_supervisor_find_app_by_id_internal(struct ladish_app_supervisor * supervisor_ptr, uint64_t id)
191 struct list_head * node_ptr;
192 struct ladish_app * app_ptr;
194 list_for_each(node_ptr, &supervisor_ptr->applist)
196 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
197 if (app_ptr->id == id)
199 return app_ptr;
203 return NULL;
206 void remove_app_internal(struct ladish_app_supervisor * supervisor_ptr, struct ladish_app * app_ptr)
208 ASSERT(app_ptr->pid == 0); /* Removing not-stoped app? Zombies will make a rebellion! */
210 list_del(&app_ptr->siblings);
212 supervisor_ptr->version++;
214 cdbus_signal_emit(
215 cdbus_g_dbus_connection,
216 supervisor_ptr->opath,
217 IFACE_APP_SUPERVISOR,
218 "AppRemoved",
219 "tt",
220 &supervisor_ptr->version,
221 &app_ptr->id);
223 free(app_ptr->dbus_name);
224 free(app_ptr->name);
225 free(app_ptr->commandline);
226 free(app_ptr->js_commandline);
227 free(app_ptr);
230 void emit_app_state_changed(struct ladish_app_supervisor * supervisor_ptr, struct ladish_app * app_ptr)
232 dbus_bool_t running;
233 dbus_bool_t terminal;
234 const char * level_str;
235 uint8_t level_byte;
237 running = app_ptr->pid != 0;
238 terminal = app_ptr->terminal;
239 level_str = app_ptr->level;
240 level_byte = ladish_level_string_to_integer(app_ptr->level);
242 supervisor_ptr->version++;
244 cdbus_signal_emit(
245 cdbus_g_dbus_connection,
246 supervisor_ptr->opath,
247 IFACE_APP_SUPERVISOR,
248 "AppStateChanged",
249 "ttsbby",
250 &supervisor_ptr->version,
251 &app_ptr->id,
252 &app_ptr->name,
253 &running,
254 &terminal,
255 &level_byte);
257 cdbus_signal_emit(
258 cdbus_g_dbus_connection,
259 supervisor_ptr->opath,
260 IFACE_APP_SUPERVISOR,
261 "AppStateChanged2",
262 "ttsbbs",
263 &supervisor_ptr->version,
264 &app_ptr->id,
265 &app_ptr->name,
266 &running,
267 &terminal,
268 &level_str);
271 static void ladish_js_save_complete(struct ladish_app_supervisor * supervisor_ptr)
273 struct list_head * node_ptr;
274 struct ladish_app * app_ptr;
275 bool success;
277 ASSERT(supervisor_ptr->js_temp_dir != NULL);
278 ASSERT(supervisor_ptr->js_dir != NULL);
279 ASSERT(supervisor_ptr->pending_js_saves == 0);
281 /* find whether all strdup() calls for new commandlines succeeded */
282 list_for_each(node_ptr, &supervisor_ptr->applist)
284 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
285 if (app_ptr->state == LADISH_APP_STATE_STARTED &&
286 strcmp(app_ptr->level, LADISH_APP_LEVEL_JACKSESSION) == 0 &&
287 app_ptr->js_commandline == NULL)
288 { /* a strdup() call has failed, or js save failed for some other reason,
289 free js commandline buffers allocated by succeeded strdup() calls */
290 list_for_each(node_ptr, &supervisor_ptr->applist)
292 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
293 free(app_ptr->js_commandline);
294 app_ptr->js_commandline = NULL;
296 success = false;
297 goto fail_rm_temp_dir;
301 /* move js_dir to js_dir.1; move js_temp_dir to js_dir */
302 success = ladish_rotate(supervisor_ptr->js_temp_dir, supervisor_ptr->js_dir, 10);
303 if (!success)
305 goto fail_rm_temp_dir;
308 list_for_each(node_ptr, &supervisor_ptr->applist)
310 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
311 if (app_ptr->state == LADISH_APP_STATE_STARTED &&
312 strcmp(app_ptr->level, LADISH_APP_LEVEL_JACKSESSION) == 0)
314 ASSERT(app_ptr->commandline != NULL);
315 ASSERT(app_ptr->js_commandline != NULL);
316 free(app_ptr->commandline);
317 app_ptr->commandline = app_ptr->js_commandline;
318 app_ptr->js_commandline = NULL;
322 goto done;
324 fail_rm_temp_dir:
325 if (!ladish_rmdir_recursive(supervisor_ptr->js_temp_dir))
327 log_error("Cannot remove JS temp dir '%s'", supervisor_ptr->js_temp_dir);
330 done:
331 free(supervisor_ptr->js_temp_dir);
332 supervisor_ptr->js_temp_dir = NULL;
334 supervisor_ptr->save_callback(supervisor_ptr->save_callback_context, success);
335 supervisor_ptr->save_callback = NULL;
336 supervisor_ptr->save_callback_context = NULL;
339 #define supervisor_ptr ((struct ladish_app_supervisor *)supervisor_handle)
341 const char * ladish_app_supervisor_get_opath(ladish_app_supervisor_handle supervisor_handle)
343 return supervisor_ptr->opath;
346 ladish_app_handle ladish_app_supervisor_find_app_by_name(ladish_app_supervisor_handle supervisor_handle, const char * name)
348 struct list_head * node_ptr;
349 struct ladish_app * app_ptr;
351 list_for_each(node_ptr, &supervisor_ptr->applist)
353 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
354 if (strcmp(app_ptr->name, name) == 0)
356 return (ladish_app_handle)app_ptr;
360 return NULL;
363 ladish_app_handle ladish_app_supervisor_find_app_by_id(ladish_app_supervisor_handle supervisor_handle, uint64_t id)
365 return (ladish_app_handle)ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
368 ladish_app_handle ladish_app_supervisor_find_app_by_pid(ladish_app_supervisor_handle supervisor_handle, pid_t pid)
370 struct list_head * node_ptr;
371 struct ladish_app * app_ptr;
373 list_for_each(node_ptr, &supervisor_ptr->applist)
375 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
376 if (app_ptr->pid == pid)
378 //log_info("app \"%s\" found by pid %llu", app_ptr->name, (unsigned long long)pid);
379 return (ladish_app_handle)app_ptr;
383 return NULL;
386 ladish_app_handle ladish_app_supervisor_find_app_by_uuid(ladish_app_supervisor_handle supervisor_handle, const uuid_t uuid)
388 struct list_head * node_ptr;
389 struct ladish_app * app_ptr;
391 list_for_each(node_ptr, &supervisor_ptr->applist)
393 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
394 if (uuid_compare(app_ptr->uuid, uuid) == 0)
396 return (ladish_app_handle)app_ptr;
400 return NULL;
403 ladish_app_handle
404 ladish_app_supervisor_add(
405 ladish_app_supervisor_handle supervisor_handle,
406 const char * name,
407 uuid_t uuid,
408 bool autorun,
409 const char * command,
410 bool terminal,
411 const char * level)
413 struct ladish_app * app_ptr;
414 dbus_bool_t running;
415 dbus_bool_t dbus_terminal;
416 size_t len;
417 uint8_t level_byte;
419 if (!ladish_check_app_level_validity(level, &len))
421 log_error("invalid level '%s'", level);
422 goto fail;
425 if (strcmp(level, LADISH_APP_LEVEL_JACKSESSION) == 0 &&
426 supervisor_ptr->dir == NULL)
428 log_error("jack session apps need directory");
429 goto fail;
432 app_ptr = malloc(sizeof(struct ladish_app));
433 if (app_ptr == NULL)
435 log_error("malloc of struct ladish_app failed");
436 goto fail;
439 app_ptr->name = strdup(name);
440 if (app_ptr->name == NULL)
442 log_error("strdup() failed for app name");
443 goto free_app;
446 app_ptr->commandline = strdup(command);
447 if (app_ptr->commandline == NULL)
449 log_error("strdup() failed for app commandline");
450 goto free_name;
453 app_ptr->js_commandline = NULL;
455 app_ptr->dbus_name = NULL;
457 app_ptr->terminal = terminal;
458 memcpy(app_ptr->level, level, len + 1);
459 app_ptr->pid = 0;
460 app_ptr->pgrp = 0;
461 app_ptr->firstborn_pid = 0;
462 app_ptr->firstborn_pgrp = 0;
463 app_ptr->firstborn_refcount = 0;
465 app_ptr->id = supervisor_ptr->next_id++;
466 if (uuid == NULL || uuid_is_null(uuid))
468 uuid_generate(app_ptr->uuid);
470 else
472 uuid_copy(app_ptr->uuid, uuid);
475 app_ptr->zombie = false;
476 app_ptr->state = LADISH_APP_STATE_STOPPED;
477 app_ptr->autorun = autorun;
478 app_ptr->supervisor = supervisor_ptr;
479 list_add_tail(&app_ptr->siblings, &supervisor_ptr->applist);
481 supervisor_ptr->version++;
483 running = false;
484 dbus_terminal = terminal;
485 level = app_ptr->level;
486 level_byte = ladish_level_string_to_integer(app_ptr->level);
487 cdbus_signal_emit(
488 cdbus_g_dbus_connection,
489 supervisor_ptr->opath,
490 IFACE_APP_SUPERVISOR,
491 "AppAdded",
492 "ttsbby",
493 &supervisor_ptr->version,
494 &app_ptr->id,
495 &app_ptr->name,
496 &running,
497 &dbus_terminal,
498 &level_byte);
499 cdbus_signal_emit(
500 cdbus_g_dbus_connection,
501 supervisor_ptr->opath,
502 IFACE_APP_SUPERVISOR,
503 "AppAdded2",
504 "ttsbbs",
505 &supervisor_ptr->version,
506 &app_ptr->id,
507 &app_ptr->name,
508 &running,
509 &dbus_terminal,
510 &level);
512 return (ladish_app_handle)app_ptr;
514 free_name:
515 free(app_ptr->name);
516 free_app:
517 free(app_ptr);
518 fail:
519 return NULL;
522 static void ladish_app_send_signal(struct ladish_app * app_ptr, int sig, bool prefer_firstborn)
524 pid_t pid;
525 const char * signal_name;
527 ASSERT(app_ptr->state = LADISH_APP_STATE_STARTED);
529 switch (sig)
531 case SIGTERM:
532 signal_name = "SIGTERM";
533 break;
534 case SIGKILL:
535 signal_name = "SIGKILL";
536 break;
537 case SIGUSR1:
538 signal_name = "SIGUSR1";
539 break;
540 default:
541 signal_name = strsignal(sig);
542 if (signal_name == NULL)
544 signal_name = "unknown";
548 if (app_ptr->pid == 0)
550 log_error("not sending signal %d (%s) to app '%s' because its pid is %d", sig, signal_name, app_ptr->name, (int)app_ptr->pid);
551 ASSERT_NO_PASS;
552 return;
555 switch (sig)
557 case SIGKILL:
558 case SIGTERM:
559 if (app_ptr->pgrp == 0)
561 app_ptr->pgrp = getpgid(app_ptr->pid);
562 if (app_ptr->pgrp == -1)
564 app_ptr->pgrp = 0;
565 if (errno != ESRCH)
567 log_error("getpgid(%u) failed. %s (%d)", (unsigned int)app_ptr->pid, strerror(errno), errno);
572 if (app_ptr->firstborn_pid != 0)
574 app_ptr->firstborn_pgrp = getpgid(app_ptr->firstborn_pid);
575 if (app_ptr->firstborn_pgrp == -1)
577 app_ptr->firstborn_pgrp = 0;
578 if (errno != ESRCH)
580 log_error("getpgid(%u) failed (firstborn). %s (%d)", (unsigned int)app_ptr->firstborn_pid, strerror(errno), errno);
585 if (app_ptr->pgrp != 0)
587 log_info("sending signal %d (%s) to pgrp %u ('%s')", sig, signal_name, (unsigned int)app_ptr->pgrp, app_ptr->name);
589 if (app_ptr->pgrp <= 1)
591 ASSERT_NO_PASS;
592 return;
595 killpg(app_ptr->pgrp, sig);
597 if (app_ptr->firstborn_pid != 0)
599 if (app_ptr->firstborn_pgrp != 0)
601 if (app_ptr->firstborn_pgrp <= 1)
603 ASSERT_NO_PASS;
604 return;
607 if (app_ptr->firstborn_pgrp != app_ptr->pgrp)
609 log_info("sending signal %d (%s) to firstborn pgrp %u ('%s')", sig, signal_name, (unsigned int)app_ptr->firstborn_pgrp, app_ptr->name);
611 killpg(app_ptr->firstborn_pgrp, sig);
613 return;
615 /* fall through to sending signal to pid */
617 else
619 return;
622 /* fall through to sending signal to pid */
623 default:
624 if (app_ptr->pid <= 1)
626 ASSERT_NO_PASS;
627 return;
630 if (prefer_firstborn && app_ptr->firstborn_pid != 0)
632 pid = app_ptr->firstborn_pid;
634 else
636 pid = app_ptr->pid;
639 if (pid <= 1)
641 ASSERT_NO_PASS;
642 return;
645 log_info("sending signal %d (%s) to '%s' with pid %u", sig, signal_name, app_ptr->name, (unsigned int)pid);
646 kill(pid, sig);
650 static inline void ladish_app_initiate_lash_save(struct ladish_app * app_ptr, const char * base_dir)
652 char * app_dir;
653 char uuid_str[37];
655 if (base_dir == NULL)
657 log_error("Cannot initiate LASH save because base dir is unknown");
658 ASSERT_NO_PASS;
659 goto exit;
662 uuid_unparse(app_ptr->uuid, uuid_str);
663 app_dir = catdup3(base_dir, "/lash_apps/", uuid_str);
664 if (app_dir == NULL)
666 log_error("Cannot initiate LASH save because of memory allocation failure.");
667 goto exit;
670 if (!ensure_dir_exist(app_dir, S_IRWXU | S_IRWXG | S_IRWXO))
672 goto free;
675 if (lash_client_proxy_save(app_ptr->dbus_name, app_dir))
677 log_info("LASH Save into '%s' initiated for '%s' with D-Bus name '%s'", app_dir, app_ptr->name, app_ptr->dbus_name);
680 free:
681 free(app_dir);
682 exit:
683 return;
686 static inline void ladish_app_initiate_lash_restore(struct ladish_app * app_ptr, const char * base_dir)
688 char * app_dir;
689 char uuid_str[37];
690 struct st;
692 if (base_dir == NULL)
694 log_error("Cannot initiate LASH restore because base dir is unknown");
695 ASSERT_NO_PASS;
696 goto exit;
699 uuid_unparse(app_ptr->uuid, uuid_str);
700 app_dir = catdup3(base_dir, "/lash_apps/", uuid_str);
701 if (app_dir == NULL)
703 log_error("Cannot initiate LASH restore because of memory allocation failure.");
704 goto exit;
707 if (!check_dir_exists(app_dir))
709 log_info("Not initiating LASH restore because of app directory '%s' does not exist.", app_dir);
710 goto free;
713 if (lash_client_proxy_restore(app_ptr->dbus_name, app_dir))
715 log_info("LASH Save from '%s' initiated for '%s' with D-Bus name '%s'", app_dir, app_ptr->name, app_ptr->dbus_name);
718 free:
719 free(app_dir);
720 exit:
721 return;
724 #define app_ptr ((struct ladish_app *)context)
726 static void ladish_js_app_save_complete(void * context, const char * commandline)
728 if (commandline != NULL)
730 log_info("JS app saved, commandline '%s'", commandline);
731 ASSERT(app_ptr->supervisor->js_temp_dir != NULL);
733 ASSERT(app_ptr->js_commandline == NULL);
734 app_ptr->js_commandline = strdup(commandline);
735 if (app_ptr->js_commandline == NULL)
737 log_error("strdup() failed for JS app '%s' commandline '%s'", app_ptr->name, commandline);
740 else
742 ASSERT(app_ptr->js_commandline == NULL);
743 log_error("JACK session save failed for JS app '%s'", app_ptr->name);
746 if (app_ptr->supervisor->pending_js_saves != 1)
748 ASSERT(app_ptr->supervisor->pending_js_saves > 1);
749 app_ptr->supervisor->pending_js_saves--;
750 log_info("%u more JS apps are still saving", app_ptr->supervisor->pending_js_saves);
751 return;
754 log_info("No more pending JS app saves");
755 app_ptr->supervisor->pending_js_saves = 0;
757 ladish_js_save_complete(app_ptr->supervisor);
760 #undef app_ptr
762 static inline void ladish_app_initiate_save(struct ladish_app * app_ptr)
764 if (strcmp(app_ptr->level, LADISH_APP_LEVEL_LASH) == 0 &&
765 app_ptr->dbus_name != NULL)
767 ladish_app_initiate_lash_save(app_ptr, app_ptr->supervisor->dir != NULL ? app_ptr->supervisor->dir : g_base_dir);
769 else if (strcmp(app_ptr->level, LADISH_APP_LEVEL_JACKSESSION) == 0)
771 log_info("Initiating JACK session save for '%s'", app_ptr->name);
772 if (!ladish_js_save_app(app_ptr->uuid, app_ptr->supervisor->js_temp_dir, app_ptr, ladish_js_app_save_complete))
774 ladish_js_app_save_complete(app_ptr, NULL);
777 else if (strcmp(app_ptr->level, LADISH_APP_LEVEL_1) == 0)
779 ladish_app_send_signal(app_ptr, SIGUSR1, true);
783 static inline void ladish_app_initiate_stop(struct ladish_app * app_ptr)
785 if (strcmp(app_ptr->level, LADISH_APP_LEVEL_LASH) == 0 &&
786 app_ptr->dbus_name != NULL &&
787 lash_client_proxy_quit(app_ptr->dbus_name))
789 log_info("LASH Quit initiated for '%s' with D-Bus name '%s'", app_ptr->name, app_ptr->dbus_name);
791 else
793 ladish_app_send_signal(app_ptr, SIGTERM, false);
796 app_ptr->state = LADISH_APP_STATE_STOPPING;
799 bool ladish_app_supervisor_clear(ladish_app_supervisor_handle supervisor_handle)
801 struct list_head * node_ptr;
802 struct list_head * safe_node_ptr;
803 struct ladish_app * app_ptr;
804 bool lifeless;
806 free(supervisor_ptr->js_temp_dir);
807 supervisor_ptr->js_temp_dir = NULL;
808 free(supervisor_ptr->js_dir);
809 supervisor_ptr->js_dir = NULL;
810 free(supervisor_ptr->dir);
811 supervisor_ptr->dir = NULL;
813 free(supervisor_ptr->project_name);
814 supervisor_ptr->project_name = NULL;
816 lifeless = true;
818 list_for_each_safe(node_ptr, safe_node_ptr, &supervisor_ptr->applist)
820 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
821 if (app_ptr->pid != 0)
823 log_info("terminating '%s'...", app_ptr->name);
824 ladish_app_initiate_stop(app_ptr);
825 app_ptr->zombie = true;
826 lifeless = false;
828 else
830 log_info("removing '%s'", app_ptr->name);
831 remove_app_internal(supervisor_ptr, app_ptr);
835 return lifeless;
838 void ladish_app_supervisor_destroy(ladish_app_supervisor_handle supervisor_handle)
840 ladish_app_supervisor_clear(supervisor_handle);
841 free(supervisor_ptr->name);
842 free(supervisor_ptr->opath);
843 free(supervisor_ptr);
846 bool
847 ladish_app_supervisor_set_directory(
848 ladish_app_supervisor_handle supervisor_handle,
849 const char * dir)
851 char * dup;
852 char * js_dir;
854 ASSERT(supervisor_ptr->pending_js_saves == 0);
856 dup = strdup(dir);
857 if (dup == NULL)
859 log_error("strdup(\"%s\") failed", dir);
860 return false;
863 js_dir = catdup(dir, "/js_apps");
864 if (js_dir == NULL)
866 log_error("catdup() failed to compose supervisor js dir path");
867 free(dup);
868 return false;
871 free(supervisor_ptr->dir);
872 supervisor_ptr->dir = dup;
874 free(supervisor_ptr->js_dir);
875 supervisor_ptr->js_dir = js_dir;
877 return true;
880 bool
881 ladish_app_supervisor_set_project_name(
882 ladish_app_supervisor_handle supervisor_handle,
883 const char * project_name)
885 char * dup;
887 if (project_name != NULL)
889 dup = strdup(project_name);
890 if (dup == NULL)
892 log_error("strdup(\"%s\") failed", project_name);
893 return false;
896 else
898 dup = NULL;
901 free(supervisor_ptr->project_name);
902 supervisor_ptr->project_name = dup;
904 return true;
907 bool ladish_app_supervisor_child_exit(ladish_app_supervisor_handle supervisor_handle, pid_t pid, int exit_status)
909 struct list_head * node_ptr;
910 struct ladish_app * app_ptr;
911 bool clean;
913 list_for_each(node_ptr, &supervisor_ptr->applist)
915 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
916 if (app_ptr->pid == pid)
918 clean = WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0;
920 log_info("%s exit of child '%s' detected.", clean ? "clean" : "dirty", app_ptr->name);
922 app_ptr->pid = 0;
923 app_ptr->pgrp = 0;
924 /* firstborn pid and pgrp is not reset here because it is refcounted
925 and managed independently through the add/del_pid() methods */
927 if (app_ptr->zombie)
929 remove_app_internal(supervisor_ptr, app_ptr);
931 else
933 if (app_ptr->state == LADISH_APP_STATE_STARTED && !clean)
935 ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "App terminated unexpectedly", app_ptr->name);
938 app_ptr->state = LADISH_APP_STATE_STOPPED;
940 emit_app_state_changed(supervisor_ptr, app_ptr);
943 return true;
947 return false;
950 bool
951 ladish_app_supervisor_enum(
952 ladish_app_supervisor_handle supervisor_handle,
953 void * context,
954 ladish_app_supervisor_enum_callback callback)
956 struct list_head * node_ptr;
957 struct ladish_app * app_ptr;
959 list_for_each(node_ptr, &supervisor_ptr->applist)
961 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
963 if (!callback(context, app_ptr->name, app_ptr->pid != 0, app_ptr->commandline, app_ptr->terminal, app_ptr->level, app_ptr->pid, app_ptr->uuid))
965 return false;
969 return true;
972 #define app_ptr ((struct ladish_app *)app_handle)
974 bool ladish_app_supervisor_start_app(ladish_app_supervisor_handle supervisor_handle, ladish_app_handle app_handle)
976 char uuid_str[37];
977 char * js_dir;
978 bool ret;
979 bool set_env_vars;
981 app_ptr->zombie = false;
983 ASSERT(app_ptr->pid == 0);
985 set_env_vars = ladish_level_string_to_integer(app_ptr->level) >= 2;
987 if (strcmp(app_ptr->level, LADISH_APP_LEVEL_JACKSESSION) == 0)
989 uuid_unparse(app_ptr->uuid, uuid_str);
990 js_dir = catdup4(supervisor_ptr->js_dir, "/", uuid_str, "/");
991 if (js_dir == NULL)
993 log_error("catdup4() failed to compose app jack session dir");
994 return false;
997 else
999 js_dir = NULL;
1002 ret = loader_execute(
1003 supervisor_ptr->name,
1004 supervisor_ptr->project_name,
1005 app_ptr->name,
1006 supervisor_ptr->dir != NULL ? supervisor_ptr->dir : "/",
1007 js_dir,
1008 app_ptr->terminal,
1009 app_ptr->commandline,
1010 set_env_vars,
1011 &app_ptr->pid);
1013 free(js_dir);
1015 if (!ret)
1017 return false;
1020 ASSERT(app_ptr->pid != 0);
1021 app_ptr->state = LADISH_APP_STATE_STARTED;
1023 emit_app_state_changed(supervisor_ptr, app_ptr);
1024 return true;
1027 void ladish_app_supervisor_remove_app(ladish_app_supervisor_handle supervisor_handle, ladish_app_handle app_handle)
1029 remove_app_internal(supervisor_ptr, app_ptr);
1032 unsigned int ladish_app_get_state(ladish_app_handle app_handle)
1034 return app_ptr->state;
1037 bool ladish_app_is_running(ladish_app_handle app_handle)
1039 return app_ptr->pid != 0;
1042 const char * ladish_app_get_name(ladish_app_handle app_handle)
1044 return app_ptr->name;
1047 void ladish_app_get_uuid(ladish_app_handle app_handle, uuid_t uuid)
1049 uuid_copy(uuid, app_ptr->uuid);
1052 void ladish_app_stop(ladish_app_handle app_handle)
1054 ladish_app_initiate_stop(app_ptr);
1057 void ladish_app_kill(ladish_app_handle app_handle)
1059 ladish_app_send_signal(app_ptr, SIGKILL, false);
1060 app_ptr->state = LADISH_APP_STATE_KILL;
1063 void ladish_app_restore(ladish_app_handle app_handle)
1065 if (strcmp(app_ptr->level, LADISH_APP_LEVEL_LASH) == 0 &&
1066 app_ptr->dbus_name != NULL)
1068 ladish_app_initiate_lash_restore(app_ptr, app_ptr->supervisor->dir != NULL ? app_ptr->supervisor->dir : g_base_dir);
1072 void ladish_app_add_pid(ladish_app_handle app_handle, pid_t pid)
1074 if (app_ptr->pid == 0)
1076 log_error("Associating pid with stopped app does not make sense");
1077 ASSERT_NO_PASS;
1078 return;
1081 if (pid <= 1) /* catch -1, 0 and 1 */
1083 log_error("Refusing domination by ignoring pid %d", (int)pid);
1084 ASSERT_NO_PASS;
1085 return;
1088 if (app_ptr->pid == pid)
1089 { /* The top level process that is already known */
1090 return;
1093 if (app_ptr->firstborn_pid != 0)
1094 { /* Ignore non-first children */
1095 if (app_ptr->firstborn_pid == pid)
1097 app_ptr->firstborn_refcount++;
1099 return;
1102 log_info("First grandchild with pid %u", (unsigned int)pid);
1103 app_ptr->firstborn_pid = pid;
1104 ASSERT(app_ptr->firstborn_refcount == 0);
1105 app_ptr->firstborn_refcount = 1;
1108 void ladish_app_del_pid(ladish_app_handle app_handle, pid_t pid)
1110 if (app_ptr->firstborn_pid != 0 && app_ptr->firstborn_pid == pid)
1112 ASSERT(app_ptr->firstborn_refcount > 0);
1113 app_ptr->firstborn_refcount--;
1114 if (app_ptr->firstborn_refcount > 0)
1116 return;
1118 log_info("First grandchild with pid %u has gone", (unsigned int)pid);
1119 app_ptr->firstborn_pid = 0;
1120 app_ptr->firstborn_pgrp = 0;
1121 app_ptr->firstborn_refcount = 0;
1125 bool ladish_app_set_dbus_name(ladish_app_handle app_handle, const char * name)
1127 char * dup;
1129 dup = strdup(name);
1130 if (dup == NULL)
1132 log_error("strdup() failed for app dbus name");
1133 return false;
1136 free(app_ptr->dbus_name);
1137 app_ptr->dbus_name = dup;
1138 return true;
1141 #undef app_ptr
1143 void ladish_app_supervisor_autorun(ladish_app_supervisor_handle supervisor_handle)
1145 struct list_head * node_ptr;
1146 struct ladish_app * app_ptr;
1148 list_for_each(node_ptr, &supervisor_ptr->applist)
1150 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
1152 if (!app_ptr->autorun)
1154 continue;
1157 app_ptr->autorun = false;
1159 log_info("autorun('%s', %s, '%s') called", app_ptr->name, app_ptr->terminal ? "terminal" : "shell", app_ptr->commandline);
1161 if (!ladish_app_supervisor_start_app((ladish_app_supervisor_handle)supervisor_ptr, (ladish_app_handle)app_ptr))
1163 log_error("Execution of '%s' failed", app_ptr->commandline);
1164 return;
1169 void ladish_app_supervisor_stop(ladish_app_supervisor_handle supervisor_handle)
1171 struct list_head * node_ptr;
1172 struct ladish_app * app_ptr;
1174 list_for_each(node_ptr, &supervisor_ptr->applist)
1176 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
1177 if (app_ptr->pid != 0)
1179 log_info("terminating '%s'...", app_ptr->name);
1180 app_ptr->autorun = true;
1181 ladish_app_initiate_stop(app_ptr);
1186 void
1187 ladish_app_supervisor_save(
1188 ladish_app_supervisor_handle supervisor_handle,
1189 void * context,
1190 ladish_save_complete_callback callback)
1192 struct list_head * node_ptr;
1193 struct ladish_app * app_ptr;
1194 bool success;
1196 ASSERT(callback != NULL);
1198 ASSERT(supervisor_ptr->js_temp_dir == NULL);
1199 ASSERT(supervisor_ptr->pending_js_saves == 0);
1200 list_for_each(node_ptr, &supervisor_ptr->applist)
1202 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
1203 if (app_ptr->state == LADISH_APP_STATE_STARTED &&
1204 strcmp(app_ptr->level, LADISH_APP_LEVEL_JACKSESSION) == 0)
1206 ASSERT(app_ptr->js_commandline == NULL);
1207 supervisor_ptr->pending_js_saves++;
1211 supervisor_ptr->save_callback_context = context;
1212 supervisor_ptr->save_callback = callback;
1214 if (supervisor_ptr->pending_js_saves > 0)
1216 ASSERT(supervisor_ptr->dir != NULL);
1217 supervisor_ptr->js_temp_dir = catdup(supervisor_ptr->dir, "/js_apps.tmpXXXXXX");
1218 if (supervisor_ptr->js_temp_dir == NULL)
1220 log_error("catdup() failed to compose supervisor js temp dir path template");
1221 goto reset_js_pending_saves;
1224 if (mkdtemp(supervisor_ptr->js_temp_dir) == NULL)
1226 log_error("mkdtemp('%s') failed. errno = %d (%s)", supervisor_ptr->js_temp_dir, errno, strerror(errno));
1227 goto free_js_temp_dir;
1230 log_info("saving %u JACK session apps to '%s'", supervisor_ptr->pending_js_saves, supervisor_ptr->js_temp_dir);
1233 list_for_each(node_ptr, &supervisor_ptr->applist)
1235 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
1236 if (app_ptr->state != LADISH_APP_STATE_STARTED)
1238 continue;
1241 if (app_ptr->pid == 0)
1243 ASSERT_NO_PASS;
1244 continue;
1247 ladish_app_initiate_save(app_ptr);
1250 success = true;
1251 goto exit;
1253 free_js_temp_dir:
1254 free(supervisor_ptr->js_temp_dir);
1255 supervisor_ptr->js_temp_dir = NULL;
1256 reset_js_pending_saves:
1257 supervisor_ptr->pending_js_saves = 0;
1258 success = false;
1259 exit:
1260 if (supervisor_ptr->pending_js_saves == 0 && supervisor_ptr->save_callback != NULL)
1261 { /* Room/studio without js apps.
1262 In case of ladish_js_save_app() failure,
1263 the callback will either be called already or
1264 will be called later when all pending js app saves are done.
1265 In former case callback will be NULL.
1266 In latter case pending_js_saves will be greater than zero. */
1267 ASSERT(supervisor_ptr->save_callback == callback);
1268 ASSERT(supervisor_ptr->save_callback_context = context);
1269 callback(context, success);
1270 supervisor_ptr->save_callback = NULL;
1271 supervisor_ptr->save_callback_context = NULL;
1275 const char * ladish_app_supervisor_get_name(ladish_app_supervisor_handle supervisor_handle)
1277 return supervisor_ptr->name;
1280 unsigned int ladish_app_supervisor_get_running_app_count(ladish_app_supervisor_handle supervisor_handle)
1282 struct list_head * node_ptr;
1283 struct ladish_app * app_ptr;
1284 unsigned int counter;
1286 counter = 0;
1287 list_for_each(node_ptr, &supervisor_ptr->applist)
1289 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
1290 if (app_ptr->pid != 0)
1292 counter++;
1296 return counter;
1299 bool ladish_app_supervisor_has_apps(ladish_app_supervisor_handle supervisor_handle)
1301 return !list_empty(&supervisor_ptr->applist);
1304 void ladish_app_supervisor_dump(ladish_app_supervisor_handle supervisor_handle)
1306 struct list_head * node_ptr;
1307 struct ladish_app * app_ptr;
1308 char uuid_str[37];
1310 list_for_each(node_ptr, &supervisor_ptr->applist)
1312 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
1313 uuid_unparse(app_ptr->uuid, uuid_str);
1314 log_info("app '%s' with commandline '%s'", app_ptr->name, app_ptr->commandline);
1315 log_info(" %s", uuid_str);
1316 log_info(" %s, %s, level '%s'", app_ptr->terminal ? "terminal" : "shell", app_ptr->autorun ? "autorun" : "stopped", app_ptr->level);
1320 #undef supervisor_ptr
1322 /**********************************************************************************/
1323 /* D-Bus methods */
1324 /**********************************************************************************/
1326 #define supervisor_ptr ((struct ladish_app_supervisor *)call_ptr->iface_context)
1328 static void get_version(struct cdbus_method_call * call_ptr)
1330 uint32_t version;
1332 version = 1;
1334 cdbus_method_return_new_single(call_ptr, DBUS_TYPE_UINT32, &version);
1337 static void get_all_multiversion(struct cdbus_method_call * call_ptr, int version)
1339 DBusMessageIter iter, array_iter, struct_iter;
1340 struct list_head * node_ptr;
1341 struct ladish_app * app_ptr;
1342 dbus_bool_t running;
1343 dbus_bool_t terminal;
1344 const char * level_str;
1345 uint8_t level_byte;
1347 //log_info("get_all called");
1349 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
1350 if (call_ptr->reply == NULL)
1352 goto fail;
1355 dbus_message_iter_init_append(call_ptr->reply, &iter);
1357 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, &supervisor_ptr->version))
1359 goto fail_unref;
1362 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, version == 1 ? "(tsbby)" : "(tsbbs)", &array_iter))
1364 goto fail_unref;
1367 list_for_each(node_ptr, &supervisor_ptr->applist)
1369 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
1371 log_info("app '%s' (%llu)", app_ptr->name, (unsigned long long)app_ptr->id);
1373 if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
1375 goto fail_unref;
1378 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &app_ptr->id))
1380 goto fail_unref;
1383 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &app_ptr->name))
1385 goto fail_unref;
1388 running = app_ptr->pid != 0;
1389 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BOOLEAN, &running))
1391 goto fail_unref;
1394 terminal = app_ptr->terminal;
1395 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BOOLEAN, &terminal))
1397 goto fail_unref;
1400 if (version == 1)
1402 level_byte = ladish_level_string_to_integer(app_ptr->level);
1403 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &level_byte))
1405 goto fail_unref;
1408 else
1410 ASSERT(version == 2);
1412 level_str = app_ptr->level;
1413 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &level_str))
1415 goto fail_unref;
1419 if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
1421 goto fail_unref;
1425 if (!dbus_message_iter_close_container(&iter, &array_iter))
1427 goto fail_unref;
1430 return;
1432 fail_unref:
1433 dbus_message_unref(call_ptr->reply);
1434 call_ptr->reply = NULL;
1436 fail:
1437 log_error("Ran out of memory trying to construct method return");
1440 static void get_all1(struct cdbus_method_call * call_ptr)
1442 get_all_multiversion(call_ptr, 1);
1445 static void get_all2(struct cdbus_method_call * call_ptr)
1447 get_all_multiversion(call_ptr, 2);
1450 static void run_custom1(struct cdbus_method_call * call_ptr)
1452 dbus_bool_t terminal;
1453 const char * commandline;
1454 const char * name;
1455 uint8_t level;
1457 if (!dbus_message_get_args(
1458 call_ptr->message,
1459 &cdbus_g_dbus_error,
1460 DBUS_TYPE_BOOLEAN, &terminal,
1461 DBUS_TYPE_STRING, &commandline,
1462 DBUS_TYPE_STRING, &name,
1463 DBUS_TYPE_BYTE, &level,
1464 DBUS_TYPE_INVALID))
1466 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1467 dbus_error_free(&cdbus_g_dbus_error);
1468 return;
1471 log_info("%s('%s', %s, '%s', %"PRIu8") called", call_ptr->method_name, name, terminal ? "terminal" : "shell", commandline, level);
1473 if (level > 1)
1475 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "invalid integer level %"PRIu8, level);
1476 return;
1479 if (ladish_command_new_app(
1480 call_ptr,
1481 ladish_studio_get_cmd_queue(),
1482 supervisor_ptr->opath,
1483 terminal,
1484 commandline,
1485 name,
1486 level == 0 ? LADISH_APP_LEVEL_0 : LADISH_APP_LEVEL_1))
1488 cdbus_method_return_new_void(call_ptr);
1492 static void run_custom2(struct cdbus_method_call * call_ptr)
1494 dbus_bool_t terminal;
1495 const char * commandline;
1496 const char * name;
1497 const char * level;
1499 if (!dbus_message_get_args(
1500 call_ptr->message,
1501 &cdbus_g_dbus_error,
1502 DBUS_TYPE_BOOLEAN, &terminal,
1503 DBUS_TYPE_STRING, &commandline,
1504 DBUS_TYPE_STRING, &name,
1505 DBUS_TYPE_STRING, &level,
1506 DBUS_TYPE_INVALID))
1508 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1509 dbus_error_free(&cdbus_g_dbus_error);
1510 return;
1513 log_info("%s('%s', %s, '%s', '%s') called", call_ptr->method_name, name, terminal ? "terminal" : "shell", commandline, level);
1515 if (!ladish_check_app_level_validity(level, NULL))
1517 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "invalid level '%s'", level);
1518 return;
1521 if (ladish_command_new_app(
1522 call_ptr,
1523 ladish_studio_get_cmd_queue(),
1524 supervisor_ptr->opath,
1525 terminal,
1526 commandline,
1527 name,
1528 level))
1530 cdbus_method_return_new_void(call_ptr);
1534 static void start_app(struct cdbus_method_call * call_ptr)
1536 uint64_t id;
1538 if (!dbus_message_get_args(
1539 call_ptr->message,
1540 &cdbus_g_dbus_error,
1541 DBUS_TYPE_UINT64, &id,
1542 DBUS_TYPE_INVALID))
1544 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1545 dbus_error_free(&cdbus_g_dbus_error);
1546 return;
1549 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_STARTED))
1551 cdbus_method_return_new_void(call_ptr);
1555 static void stop_app(struct cdbus_method_call * call_ptr)
1557 uint64_t id;
1559 if (!dbus_message_get_args(
1560 call_ptr->message,
1561 &cdbus_g_dbus_error,
1562 DBUS_TYPE_UINT64, &id,
1563 DBUS_TYPE_INVALID))
1565 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1566 dbus_error_free(&cdbus_g_dbus_error);
1567 return;
1570 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_STOPPED))
1572 cdbus_method_return_new_void(call_ptr);
1576 static void kill_app(struct cdbus_method_call * call_ptr)
1578 uint64_t id;
1580 if (!dbus_message_get_args(
1581 call_ptr->message,
1582 &cdbus_g_dbus_error,
1583 DBUS_TYPE_UINT64, &id,
1584 DBUS_TYPE_INVALID))
1586 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1587 dbus_error_free(&cdbus_g_dbus_error);
1588 return;
1591 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_KILL))
1593 cdbus_method_return_new_void(call_ptr);
1597 static void get_app_properties_multiversion(struct cdbus_method_call * call_ptr, int version)
1599 uint64_t id;
1600 struct ladish_app * app_ptr;
1601 dbus_bool_t running;
1602 dbus_bool_t terminal;
1603 const char * level_str;
1604 uint8_t level_byte;
1605 int level_type;
1606 void * level_ptr;
1608 if (!dbus_message_get_args(
1609 call_ptr->message,
1610 &cdbus_g_dbus_error,
1611 DBUS_TYPE_UINT64, &id,
1612 DBUS_TYPE_INVALID))
1614 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1615 dbus_error_free(&cdbus_g_dbus_error);
1616 return;
1619 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
1620 if (app_ptr == NULL)
1622 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
1623 return;
1626 running = app_ptr->pid != 0;
1627 terminal = app_ptr->terminal;
1628 if (version == 1)
1630 level_byte = ladish_level_string_to_integer(app_ptr->level);
1631 level_type = DBUS_TYPE_BYTE;
1632 level_ptr = &level_byte;
1634 else
1636 ASSERT(version == 2);
1637 level_str = app_ptr->level;
1638 level_type = DBUS_TYPE_STRING;
1639 level_ptr = &level_str;
1642 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
1643 if (call_ptr->reply == NULL)
1645 goto fail;
1648 if (!dbus_message_append_args(
1649 call_ptr->reply,
1650 DBUS_TYPE_STRING, &app_ptr->name,
1651 DBUS_TYPE_STRING, &app_ptr->commandline,
1652 DBUS_TYPE_BOOLEAN, &running,
1653 DBUS_TYPE_BOOLEAN, &terminal,
1654 level_type, level_ptr,
1655 DBUS_TYPE_INVALID))
1657 goto fail_unref;
1660 return;
1662 fail_unref:
1663 dbus_message_unref(call_ptr->reply);
1664 call_ptr->reply = NULL;
1666 fail:
1667 log_error("Ran out of memory trying to construct method return");
1670 static void get_app_properties1(struct cdbus_method_call * call_ptr)
1672 get_app_properties_multiversion(call_ptr, 1);
1675 static void get_app_properties2(struct cdbus_method_call * call_ptr)
1677 get_app_properties_multiversion(call_ptr, 2);
1680 static void set_app_properties_multiversion(struct cdbus_method_call * call_ptr, int version)
1682 uint64_t id;
1683 dbus_bool_t terminal;
1684 const char * name;
1685 const char * commandline;
1686 const char * level_str;
1687 uint8_t level_byte;
1688 int level_type;
1689 void * level_ptr;
1690 struct ladish_app * app_ptr;
1691 char * name_buffer;
1692 char * commandline_buffer;
1693 size_t len;
1695 if (version == 1)
1697 level_type = DBUS_TYPE_BYTE;
1698 level_ptr = &level_byte;
1700 else
1702 ASSERT(version == 2);
1703 level_type = DBUS_TYPE_STRING;
1704 level_ptr = &level_str;
1707 if (!dbus_message_get_args(
1708 call_ptr->message,
1709 &cdbus_g_dbus_error,
1710 DBUS_TYPE_UINT64, &id,
1711 DBUS_TYPE_STRING, &name,
1712 DBUS_TYPE_STRING, &commandline,
1713 DBUS_TYPE_BOOLEAN, &terminal,
1714 level_type, level_ptr,
1715 DBUS_TYPE_INVALID))
1717 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1718 dbus_error_free(&cdbus_g_dbus_error);
1719 return;
1722 if (version == 1)
1724 if (level_byte > 1)
1726 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "invalid integer level %"PRIu8, level_byte);
1727 return;
1730 level_str = level_byte == 0 ? LADISH_APP_LEVEL_0 : LADISH_APP_LEVEL_1;
1731 len = strlen(level_str);
1733 else
1735 ASSERT(version == 2);
1736 if (!ladish_check_app_level_validity(level_str, &len))
1738 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "invalid level '%s'", level_str);
1739 return;
1743 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
1744 if (app_ptr == NULL)
1746 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
1747 return;
1750 if (app_ptr->pid != 0 && strcmp(commandline, app_ptr->commandline) != 0)
1752 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Cannot change commandline when app is running. '%s' -> '%s'", app_ptr->commandline, commandline);
1753 return;
1756 if (app_ptr->pid != 0 && ((app_ptr->terminal && !terminal) || (!app_ptr->terminal && terminal)))
1758 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Cannot change whether to run in terminal when app is running");
1759 return;
1762 if (app_ptr->pid != 0 && strcmp(app_ptr->level, level_str) != 0)
1764 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "Cannot change app level when app is running");
1765 return;
1768 if (strcmp(commandline, app_ptr->commandline) != 0)
1770 commandline_buffer = strdup(commandline);
1771 if (commandline_buffer == NULL)
1773 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "strdup() failed for app commandline");
1774 return;
1777 else
1779 commandline_buffer = NULL;
1782 if (strcmp(name, app_ptr->name) != 0)
1784 name_buffer = strdup(name);
1785 if (name_buffer == NULL)
1787 cdbus_error(call_ptr, DBUS_ERROR_FAILED, "strdup() failed for app nam");
1788 if (commandline_buffer != NULL)
1790 free(commandline_buffer);
1792 return;
1795 else
1797 name_buffer = NULL;
1800 if (name_buffer != NULL)
1802 supervisor_ptr->on_app_renamed(supervisor_ptr->on_app_renamed_context, app_ptr->uuid, app_ptr->name, name_buffer);
1803 free(app_ptr->name);
1804 app_ptr->name = name_buffer;
1807 if (commandline_buffer != NULL)
1809 free(app_ptr->commandline);
1810 app_ptr->commandline = commandline_buffer;
1813 memcpy(app_ptr->level, level_str, len + 1);
1814 app_ptr->terminal = terminal;
1816 emit_app_state_changed(supervisor_ptr, app_ptr);
1818 cdbus_method_return_new_void(call_ptr);
1821 static void set_app_properties1(struct cdbus_method_call * call_ptr)
1823 set_app_properties_multiversion(call_ptr, 1);
1826 static void set_app_properties2(struct cdbus_method_call * call_ptr)
1828 set_app_properties_multiversion(call_ptr, 2);
1832 static void remove_app(struct cdbus_method_call * call_ptr)
1834 uint64_t id;
1836 if (!dbus_message_get_args(
1837 call_ptr->message,
1838 &cdbus_g_dbus_error,
1839 DBUS_TYPE_UINT64, &id,
1840 DBUS_TYPE_INVALID))
1842 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1843 dbus_error_free(&cdbus_g_dbus_error);
1844 return;
1847 if (ladish_command_remove_app(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id))
1849 cdbus_method_return_new_void(call_ptr);
1853 static void is_app_running(struct cdbus_method_call * call_ptr)
1855 uint64_t id;
1856 struct ladish_app * app_ptr;
1857 dbus_bool_t running;
1859 if (!dbus_message_get_args(
1860 call_ptr->message,
1861 &cdbus_g_dbus_error,
1862 DBUS_TYPE_UINT64, &id,
1863 DBUS_TYPE_INVALID))
1865 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, cdbus_g_dbus_error.message);
1866 dbus_error_free(&cdbus_g_dbus_error);
1867 return;
1870 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
1871 if (app_ptr == NULL)
1873 cdbus_error(call_ptr, DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
1874 return;
1877 running = app_ptr->pid != 0;
1879 cdbus_method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &running);
1882 #undef supervisor_ptr
1884 CDBUS_METHOD_ARGS_BEGIN(GetInterfaceVersion, "Get version of this D-Bus interface")
1885 CDBUS_METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_UINT32_AS_STRING, "Interface version")
1886 CDBUS_METHOD_ARGS_END
1888 CDBUS_METHOD_ARGS_BEGIN(GetAll, "Get list of apps")
1889 CDBUS_METHOD_ARG_DESCRIBE_OUT("list_version", DBUS_TYPE_UINT64_AS_STRING, "Version of the list")
1890 CDBUS_METHOD_ARG_DESCRIBE_OUT("apps_list", "a(tsbby)", "List of apps")
1891 CDBUS_METHOD_ARGS_END
1893 CDBUS_METHOD_ARGS_BEGIN(GetAll2, "Get list of apps")
1894 CDBUS_METHOD_ARG_DESCRIBE_OUT("list_version", DBUS_TYPE_UINT64_AS_STRING, "Version of the list")
1895 CDBUS_METHOD_ARG_DESCRIBE_OUT("apps_list", "a(tsbbs)", "List of apps")
1896 CDBUS_METHOD_ARGS_END
1898 CDBUS_METHOD_ARGS_BEGIN(RunCustom, "Start application by supplying commandline")
1899 CDBUS_METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1900 CDBUS_METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1901 CDBUS_METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "Name")
1902 CDBUS_METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1903 CDBUS_METHOD_ARGS_END
1905 CDBUS_METHOD_ARGS_BEGIN(RunCustom2, "Start application by supplying commandline")
1906 CDBUS_METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1907 CDBUS_METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1908 CDBUS_METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "Name")
1909 CDBUS_METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_STRING_AS_STRING, "Level")
1910 CDBUS_METHOD_ARGS_END
1912 CDBUS_METHOD_ARGS_BEGIN(StartApp, "Start application")
1913 CDBUS_METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1914 CDBUS_METHOD_ARGS_END
1916 CDBUS_METHOD_ARGS_BEGIN(StopApp, "Stop application")
1917 CDBUS_METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1918 CDBUS_METHOD_ARGS_END
1920 CDBUS_METHOD_ARGS_BEGIN(KillApp, "Kill application")
1921 CDBUS_METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1922 CDBUS_METHOD_ARGS_END
1924 CDBUS_METHOD_ARGS_BEGIN(RemoveApp, "Remove application")
1925 CDBUS_METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1926 CDBUS_METHOD_ARGS_END
1928 CDBUS_METHOD_ARGS_BEGIN(GetAppProperties, "Get properties of an application")
1929 CDBUS_METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1930 CDBUS_METHOD_ARG_DESCRIBE_OUT("name", DBUS_TYPE_STRING_AS_STRING, "")
1931 CDBUS_METHOD_ARG_DESCRIBE_OUT("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1932 CDBUS_METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1933 CDBUS_METHOD_ARG_DESCRIBE_OUT("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1934 CDBUS_METHOD_ARG_DESCRIBE_OUT("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1935 CDBUS_METHOD_ARGS_END
1937 CDBUS_METHOD_ARGS_BEGIN(GetAppProperties2, "Get properties of an application")
1938 CDBUS_METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1939 CDBUS_METHOD_ARG_DESCRIBE_OUT("name", DBUS_TYPE_STRING_AS_STRING, "")
1940 CDBUS_METHOD_ARG_DESCRIBE_OUT("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1941 CDBUS_METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1942 CDBUS_METHOD_ARG_DESCRIBE_OUT("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1943 CDBUS_METHOD_ARG_DESCRIBE_OUT("level", DBUS_TYPE_STRING_AS_STRING, "Level")
1944 CDBUS_METHOD_ARGS_END
1946 CDBUS_METHOD_ARGS_BEGIN(SetAppProperties, "Set properties of an application")
1947 CDBUS_METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1948 CDBUS_METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "")
1949 CDBUS_METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1950 CDBUS_METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1951 CDBUS_METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1952 CDBUS_METHOD_ARGS_END
1954 CDBUS_METHOD_ARGS_BEGIN(SetAppProperties2, "Set properties of an application")
1955 CDBUS_METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1956 CDBUS_METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "")
1957 CDBUS_METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1958 CDBUS_METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1959 CDBUS_METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_STRING_AS_STRING, "Level")
1960 CDBUS_METHOD_ARGS_END
1962 CDBUS_METHOD_ARGS_BEGIN(IsAppRunning, "Check whether application is running")
1963 CDBUS_METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1964 CDBUS_METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether app is running")
1965 CDBUS_METHOD_ARGS_END
1968 CDBUS_METHODS_BEGIN
1969 CDBUS_METHOD_DESCRIBE(GetInterfaceVersion, get_version) /* sync */
1970 CDBUS_METHOD_DESCRIBE(GetAll, get_all1) /* sync */
1971 CDBUS_METHOD_DESCRIBE(GetAll2, get_all2) /* sync */
1972 CDBUS_METHOD_DESCRIBE(RunCustom, run_custom1) /* async */
1973 CDBUS_METHOD_DESCRIBE(RunCustom2, run_custom2) /* async */
1974 CDBUS_METHOD_DESCRIBE(StartApp, start_app) /* async */
1975 CDBUS_METHOD_DESCRIBE(StopApp, stop_app) /* async */
1976 CDBUS_METHOD_DESCRIBE(KillApp, kill_app) /* async */
1977 CDBUS_METHOD_DESCRIBE(GetAppProperties, get_app_properties1) /* sync */
1978 CDBUS_METHOD_DESCRIBE(GetAppProperties2, get_app_properties2) /* sync */
1979 CDBUS_METHOD_DESCRIBE(SetAppProperties, set_app_properties1) /* sync */
1980 CDBUS_METHOD_DESCRIBE(SetAppProperties2, set_app_properties2) /* sync */
1981 CDBUS_METHOD_DESCRIBE(RemoveApp, remove_app) /* sync */
1982 CDBUS_METHOD_DESCRIBE(IsAppRunning, is_app_running) /* sync */
1983 CDBUS_METHODS_END
1985 CDBUS_SIGNAL_ARGS_BEGIN(AppAdded, "")
1986 CDBUS_SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1987 CDBUS_SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1988 CDBUS_SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
1989 CDBUS_SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1990 CDBUS_SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1991 CDBUS_SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1992 CDBUS_SIGNAL_ARGS_END
1994 CDBUS_SIGNAL_ARGS_BEGIN(AppAdded2, "")
1995 CDBUS_SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1996 CDBUS_SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1997 CDBUS_SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
1998 CDBUS_SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1999 CDBUS_SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
2000 CDBUS_SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_STRING_AS_STRING, "Level")
2001 CDBUS_SIGNAL_ARGS_END
2003 CDBUS_SIGNAL_ARGS_BEGIN(AppRemoved, "")
2004 CDBUS_SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
2005 CDBUS_SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
2006 CDBUS_SIGNAL_ARGS_END
2008 CDBUS_SIGNAL_ARGS_BEGIN(AppStateChanged, "")
2009 CDBUS_SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
2010 CDBUS_SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
2011 CDBUS_SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
2012 CDBUS_SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
2013 CDBUS_SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
2014 CDBUS_SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
2015 CDBUS_SIGNAL_ARGS_END
2017 CDBUS_SIGNAL_ARGS_BEGIN(AppStateChanged2, "")
2018 CDBUS_SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
2019 CDBUS_SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
2020 CDBUS_SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
2021 CDBUS_SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
2022 CDBUS_SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
2023 CDBUS_SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_STRING_AS_STRING, "Level")
2024 CDBUS_SIGNAL_ARGS_END
2026 CDBUS_SIGNALS_BEGIN
2027 CDBUS_SIGNAL_DESCRIBE(AppAdded)
2028 CDBUS_SIGNAL_DESCRIBE(AppAdded2)
2029 CDBUS_SIGNAL_DESCRIBE(AppRemoved)
2030 CDBUS_SIGNAL_DESCRIBE(AppStateChanged)
2031 CDBUS_SIGNAL_DESCRIBE(AppStateChanged2)
2032 CDBUS_SIGNALS_END
2034 CDBUS_INTERFACE_DEFAULT_HANDLER_METHODS_AND_SIGNALS(g_iface_app_supervisor, IFACE_APP_SUPERVISOR)