ladishd: save&restore app associations of vclients. Fix for #149
[ladish.git] / daemon / app_supervisor.c
blob86eb1c289c7a034775286af13998b068b203f18f
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2009, 2010 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 <signal.h>
31 #include <unistd.h>
33 #include "app_supervisor.h"
34 #include "../dbus/error.h"
35 #include "../dbus_constants.h"
36 #include "loader.h"
37 #include "studio_internal.h"
38 #include "../proxies/notify_proxy.h"
40 struct ladish_app
42 struct list_head siblings;
43 uint64_t id;
44 uuid_t uuid;
45 char * name;
46 char * commandline;
47 bool terminal;
48 uint8_t level;
49 pid_t pid;
50 pid_t pgrp;
51 pid_t firstborn_pid;
52 pid_t firstborn_pgrp;
53 bool zombie; /* if true, remove when stopped */
54 bool autorun;
55 unsigned int state;
58 struct ladish_app_supervisor
60 char * name;
61 char * opath;
62 char * dir;
63 char * project_name;
64 uint64_t version;
65 uint64_t next_id;
66 struct list_head applist;
67 void * on_app_renamed_context;
68 ladish_app_supervisor_on_app_renamed_callback on_app_renamed;
71 bool
72 ladish_app_supervisor_create(
73 ladish_app_supervisor_handle * supervisor_handle_ptr,
74 const char * opath,
75 const char * name,
76 void * context,
77 ladish_app_supervisor_on_app_renamed_callback on_app_renamed)
79 struct ladish_app_supervisor * supervisor_ptr;
81 supervisor_ptr = malloc(sizeof(struct ladish_app_supervisor));
82 if (supervisor_ptr == NULL)
84 log_error("malloc() failed to allocate struct ladish_app_supervisor");
85 return false;
88 supervisor_ptr->opath = strdup(opath);
89 if (supervisor_ptr->opath == NULL)
91 log_error("strdup() failed for app supervisor opath");
92 free(supervisor_ptr);
93 return false;
96 supervisor_ptr->name = strdup(name);
97 if (supervisor_ptr->name == NULL)
99 log_error("strdup() failed for app supervisor name");
100 free(supervisor_ptr->opath);
101 free(supervisor_ptr);
102 return false;
105 supervisor_ptr->dir = NULL;
106 supervisor_ptr->project_name = NULL;
108 supervisor_ptr->version = 0;
109 supervisor_ptr->next_id = 1;
111 INIT_LIST_HEAD(&supervisor_ptr->applist);
113 supervisor_ptr->on_app_renamed_context = context;
114 supervisor_ptr->on_app_renamed = on_app_renamed;
116 *supervisor_handle_ptr = (ladish_app_supervisor_handle)supervisor_ptr;
118 return true;
121 struct ladish_app * ladish_app_supervisor_find_app_by_id_internal(struct ladish_app_supervisor * supervisor_ptr, uint64_t id)
123 struct list_head * node_ptr;
124 struct ladish_app * app_ptr;
126 list_for_each(node_ptr, &supervisor_ptr->applist)
128 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
129 if (app_ptr->id == id)
131 return app_ptr;
135 return NULL;
138 void remove_app_internal(struct ladish_app_supervisor * supervisor_ptr, struct ladish_app * app_ptr)
140 ASSERT(app_ptr->pid == 0); /* Removing not-stoped app? Zombies will make a rebellion! */
142 list_del(&app_ptr->siblings);
144 supervisor_ptr->version++;
146 dbus_signal_emit(
147 g_dbus_connection,
148 supervisor_ptr->opath,
149 IFACE_APP_SUPERVISOR,
150 "AppRemoved",
151 "tt",
152 &supervisor_ptr->version,
153 &app_ptr->id);
155 free(app_ptr->name);
156 free(app_ptr->commandline);
157 free(app_ptr);
160 void emit_app_state_changed(struct ladish_app_supervisor * supervisor_ptr, struct ladish_app * app_ptr)
162 dbus_bool_t running;
163 dbus_bool_t terminal;
165 running = app_ptr->pid != 0;
166 terminal = app_ptr->terminal;
168 supervisor_ptr->version++;
170 dbus_signal_emit(
171 g_dbus_connection,
172 supervisor_ptr->opath,
173 IFACE_APP_SUPERVISOR,
174 "AppStateChanged",
175 "ttsbby",
176 &supervisor_ptr->version,
177 &app_ptr->id,
178 &app_ptr->name,
179 &running,
180 &terminal,
181 &app_ptr->level);
184 #define supervisor_ptr ((struct ladish_app_supervisor *)supervisor_handle)
186 const char * ladish_app_supervisor_get_opath(ladish_app_supervisor_handle supervisor_handle)
188 return supervisor_ptr->opath;
191 ladish_app_handle ladish_app_supervisor_find_app_by_name(ladish_app_supervisor_handle supervisor_handle, const char * name)
193 struct list_head * node_ptr;
194 struct ladish_app * app_ptr;
196 list_for_each(node_ptr, &supervisor_ptr->applist)
198 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
199 if (strcmp(app_ptr->name, name) == 0)
201 return (ladish_app_handle)app_ptr;
205 return NULL;
208 ladish_app_handle ladish_app_supervisor_find_app_by_id(ladish_app_supervisor_handle supervisor_handle, uint64_t id)
210 return (ladish_app_handle)ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
213 ladish_app_handle ladish_app_supervisor_find_app_by_pid(ladish_app_supervisor_handle supervisor_handle, pid_t pid)
215 struct list_head * node_ptr;
216 struct ladish_app * app_ptr;
218 list_for_each(node_ptr, &supervisor_ptr->applist)
220 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
221 if (app_ptr->pid == pid)
223 //log_info("app \"%s\" found by pid %llu", app_ptr->name, (unsigned long long)pid);
224 return (ladish_app_handle)app_ptr;
228 return NULL;
231 ladish_app_handle ladish_app_supervisor_find_app_by_uuid(ladish_app_supervisor_handle supervisor_handle, const uuid_t uuid)
233 struct list_head * node_ptr;
234 struct ladish_app * app_ptr;
236 list_for_each(node_ptr, &supervisor_ptr->applist)
238 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
239 if (uuid_compare(app_ptr->uuid, uuid) == 0)
241 return (ladish_app_handle)app_ptr;
245 return NULL;
248 ladish_app_handle
249 ladish_app_supervisor_add(
250 ladish_app_supervisor_handle supervisor_handle,
251 const char * name,
252 uuid_t uuid,
253 bool autorun,
254 const char * command,
255 bool terminal,
256 uint8_t level)
258 struct ladish_app * app_ptr;
259 dbus_bool_t running;
261 app_ptr = malloc(sizeof(struct ladish_app));
262 if (app_ptr == NULL)
264 log_error("malloc of struct ladish_app failed");
265 return NULL;
268 app_ptr->name = strdup(name);
269 if (app_ptr->name == NULL)
271 log_error("strdup() failed for app name");
272 free(app_ptr);
273 return NULL;
276 app_ptr->commandline = strdup(command);
277 if (app_ptr->commandline == NULL)
279 log_error("strdup() failed for app commandline");
280 free(app_ptr->name);
281 free(app_ptr);
282 return NULL;
285 app_ptr->terminal = terminal;
286 app_ptr->level = level;
287 app_ptr->pid = 0;
288 app_ptr->pgrp = 0;
289 app_ptr->firstborn_pid = 0;
290 app_ptr->firstborn_pgrp = 0;
292 app_ptr->id = supervisor_ptr->next_id++;
293 if (uuid == NULL || uuid_is_null(uuid))
295 uuid_generate(app_ptr->uuid);
297 else
299 uuid_copy(app_ptr->uuid, uuid);
302 app_ptr->zombie = false;
303 app_ptr->state = LADISH_APP_STATE_STOPPED;
304 app_ptr->autorun = autorun;
305 list_add_tail(&app_ptr->siblings, &supervisor_ptr->applist);
307 supervisor_ptr->version++;
309 running = false;
310 dbus_signal_emit(
311 g_dbus_connection,
312 supervisor_ptr->opath,
313 IFACE_APP_SUPERVISOR,
314 "AppAdded",
315 "ttsbby",
316 &supervisor_ptr->version,
317 &app_ptr->id,
318 &app_ptr->name,
319 &running,
320 &terminal,
321 &app_ptr->level);
323 return (ladish_app_handle)app_ptr;
326 static void ladish_app_send_signal(struct ladish_app * app_ptr, int sig, bool prefer_firstborn)
328 pid_t pid;
329 const char * signal_name;
331 ASSERT(app_ptr->state = LADISH_APP_STATE_STARTED);
333 switch (sig)
335 case SIGTERM:
336 signal_name = "SIGTERM";
337 break;
338 case SIGKILL:
339 signal_name = "SIGKILL";
340 break;
341 case SIGUSR1:
342 signal_name = "SIGUSR1";
343 break;
344 default:
345 signal_name = strsignal(sig);
346 if (signal_name == NULL)
348 signal_name = "unknown";
352 switch (sig)
354 case SIGKILL:
355 case SIGTERM:
356 if (app_ptr->pgrp == 0)
358 app_ptr->pgrp = getpgid(app_ptr->pid);
359 if (app_ptr->pgrp == -1)
361 app_ptr->pgrp = 0;
362 if (errno != ESRCH)
364 log_error("getpgid(%u) failed. %s (%d)", (unsigned int)app_ptr->pid, strerror(errno), errno);
369 if (app_ptr->firstborn_pid != 0)
371 app_ptr->firstborn_pgrp = getpgid(app_ptr->firstborn_pid);
372 if (app_ptr->firstborn_pgrp == -1)
374 app_ptr->firstborn_pgrp = 0;
375 if (errno != ESRCH)
377 log_error("getpgid(%u) failed (firstborn). %s (%d)", (unsigned int)app_ptr->firstborn_pid, strerror(errno), errno);
382 if (app_ptr->pgrp != 0)
384 log_info("sending signal %d (%s) to pgrp %u ('%s')", sig, signal_name, (unsigned int)app_ptr->pgrp, app_ptr->name);
386 if (app_ptr->pgrp <= 1)
388 ASSERT_NO_PASS;
389 return;
392 killpg(app_ptr->pgrp, sig);
394 if (app_ptr->firstborn_pid != 0)
396 if (app_ptr->firstborn_pgrp != 0)
398 if (app_ptr->firstborn_pgrp <= 1)
400 ASSERT_NO_PASS;
401 return;
404 if (app_ptr->firstborn_pgrp != app_ptr->pgrp)
406 log_info("sending signal %d (%s) to firstborn pgrp %u ('%s')", sig, signal_name, (unsigned int)app_ptr->firstborn_pgrp, app_ptr->name);
408 killpg(app_ptr->firstborn_pgrp, sig);
410 return;
412 /* fall through to sending signal to pid */
414 else
416 return;
419 /* fall through to sending signal to pid */
420 default:
421 if (app_ptr->pid <= 1)
423 ASSERT_NO_PASS;
424 return;
427 if (prefer_firstborn && app_ptr->firstborn_pid != 0)
429 pid = app_ptr->firstborn_pid;
431 else
433 pid = app_ptr->pid;
436 if (pid <= 1)
438 ASSERT_NO_PASS;
439 return;
442 log_info("sending signal %d (%s) to '%s' with pid %u", sig, signal_name, app_ptr->name, (unsigned int)pid);
443 kill(pid, sig);
447 bool ladish_app_supervisor_clear(ladish_app_supervisor_handle supervisor_handle)
449 struct list_head * node_ptr;
450 struct list_head * safe_node_ptr;
451 struct ladish_app * app_ptr;
452 bool lifeless;
454 free(supervisor_ptr->dir);
455 supervisor_ptr->dir = NULL;
457 free(supervisor_ptr->project_name);
458 supervisor_ptr->project_name = NULL;
460 lifeless = true;
462 list_for_each_safe(node_ptr, safe_node_ptr, &supervisor_ptr->applist)
464 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
465 if (app_ptr->pid != 0)
467 log_info("terminating '%s'...", app_ptr->name);
468 ladish_app_send_signal(app_ptr, SIGTERM, false);
469 app_ptr->zombie = true;
470 app_ptr->state = LADISH_APP_STATE_STOPPING;
471 lifeless = false;
473 else
475 log_info("removing '%s'", app_ptr->name);
476 remove_app_internal(supervisor_ptr, app_ptr);
480 return lifeless;
483 void ladish_app_supervisor_destroy(ladish_app_supervisor_handle supervisor_handle)
485 ladish_app_supervisor_clear(supervisor_handle);
486 free(supervisor_ptr->name);
487 free(supervisor_ptr->opath);
488 free(supervisor_ptr);
491 bool
492 ladish_app_supervisor_set_directory(
493 ladish_app_supervisor_handle supervisor_handle,
494 const char * dir)
496 char * dup;
498 dup = strdup(dir);
499 if (dup == NULL)
501 log_error("strdup(\"%s\") failed", dir);
502 return false;
505 if (supervisor_ptr->dir != NULL)
507 free(supervisor_ptr->dir);
510 supervisor_ptr->dir = dup;
512 return true;
515 bool
516 ladish_app_supervisor_set_project_name(
517 ladish_app_supervisor_handle supervisor_handle,
518 const char * project_name)
520 char * dup;
522 if (project_name != NULL)
524 dup = strdup(project_name);
525 if (dup == NULL)
527 log_error("strdup(\"%s\") failed", project_name);
528 return false;
531 else
533 dup = NULL;
536 free(supervisor_ptr->project_name);
537 supervisor_ptr->project_name = dup;
539 return true;
542 bool ladish_app_supervisor_child_exit(ladish_app_supervisor_handle supervisor_handle, pid_t pid)
544 struct list_head * node_ptr;
545 struct ladish_app * app_ptr;
547 list_for_each(node_ptr, &supervisor_ptr->applist)
549 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
550 if (app_ptr->pid == pid)
552 log_info("exit of child '%s' detected.", app_ptr->name);
554 app_ptr->pid = 0;
555 app_ptr->pgrp = 0;
556 app_ptr->firstborn_pid = 0;
557 app_ptr->firstborn_pgrp = 0;
558 if (app_ptr->zombie)
560 remove_app_internal(supervisor_ptr, app_ptr);
562 else
564 if (app_ptr->state == LADISH_APP_STATE_STARTED)
566 ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "App terminated unexpectedly", app_ptr->name);
569 app_ptr->state = LADISH_APP_STATE_STOPPED;
571 emit_app_state_changed(supervisor_ptr, app_ptr);
574 return true;
578 return false;
581 bool
582 ladish_app_supervisor_enum(
583 ladish_app_supervisor_handle supervisor_handle,
584 void * context,
585 ladish_app_supervisor_enum_callback callback)
587 struct list_head * node_ptr;
588 struct ladish_app * app_ptr;
590 list_for_each(node_ptr, &supervisor_ptr->applist)
592 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
594 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))
596 return false;
600 return true;
603 static inline void ladish_app_save_L1_internal(struct ladish_app * app_ptr)
605 if (app_ptr->level == 1)
607 ladish_app_send_signal(app_ptr, SIGUSR1, true);
611 #define app_ptr ((struct ladish_app *)app_handle)
613 bool ladish_app_supervisor_start_app(ladish_app_supervisor_handle supervisor_handle, ladish_app_handle app_handle)
615 app_ptr->zombie = false;
617 ASSERT(app_ptr->pid == 0);
619 if (!loader_execute(
620 supervisor_ptr->name,
621 supervisor_ptr->project_name,
622 app_ptr->name,
623 supervisor_ptr->dir != NULL ? supervisor_ptr->dir : "/",
624 app_ptr->terminal,
625 app_ptr->commandline,
626 &app_ptr->pid))
628 return false;
631 ASSERT(app_ptr->pid != 0);
632 app_ptr->state = LADISH_APP_STATE_STARTED;
634 emit_app_state_changed(supervisor_ptr, app_ptr);
635 return true;
638 void ladish_app_supervisor_remove_app(ladish_app_supervisor_handle supervisor_handle, ladish_app_handle app_handle)
640 remove_app_internal(supervisor_ptr, app_ptr);
643 unsigned int ladish_app_get_state(ladish_app_handle app_handle)
645 return app_ptr->state;
648 bool ladish_app_is_running(ladish_app_handle app_handle)
650 return app_ptr->pid != 0;
653 const char * ladish_app_get_name(ladish_app_handle app_handle)
655 return app_ptr->name;
658 void ladish_app_get_uuid(ladish_app_handle app_handle, uuid_t uuid)
660 uuid_copy(uuid, app_ptr->uuid);
663 void ladish_app_stop(ladish_app_handle app_handle)
665 ladish_app_send_signal(app_ptr, SIGTERM, false);
666 app_ptr->state = LADISH_APP_STATE_STOPPING;
669 void ladish_app_kill(ladish_app_handle app_handle)
671 ladish_app_send_signal(app_ptr, SIGKILL, false);
672 app_ptr->state = LADISH_APP_STATE_KILL;
675 void ladish_app_save_L1(ladish_app_handle app_handle)
677 ladish_app_save_L1_internal(app_ptr);
680 void ladish_app_add_pid(ladish_app_handle app_handle, pid_t pid)
682 if (app_ptr->pid == 0)
684 log_error("Associating pid with stopped app does not make sense");
685 ASSERT_NO_PASS;
686 return;
689 if (pid <= 1) /* catch -1, 0 and 1 */
691 log_error("Refusing domination by ignoring pid %d", (int)pid);
692 ASSERT_NO_PASS;
693 return;
696 if (app_ptr->pid == pid)
697 { /* The top level process that is already known */
698 return;
701 if (app_ptr->firstborn_pid != 0)
702 { /* Ignore non-first children */
703 return;
706 log_info("First grandchild with pid %u", (unsigned int)pid);
707 app_ptr->firstborn_pid = pid;
710 void ladish_app_del_pid(ladish_app_handle app_handle, pid_t pid)
712 if (app_ptr->firstborn_pid != 0 && app_ptr->firstborn_pid == pid)
714 log_info("First grandchild with pid %u has gone", (unsigned int)pid);
715 app_ptr->firstborn_pid = 0;
716 app_ptr->firstborn_pgrp = 0;
720 #undef app_ptr
722 void ladish_app_supervisor_autorun(ladish_app_supervisor_handle supervisor_handle)
724 struct list_head * node_ptr;
725 struct ladish_app * app_ptr;
727 list_for_each(node_ptr, &supervisor_ptr->applist)
729 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
731 if (!app_ptr->autorun)
733 continue;
736 app_ptr->autorun = false;
738 log_info("autorun('%s', %s, '%s') called", app_ptr->name, app_ptr->terminal ? "terminal" : "shell", app_ptr->commandline);
740 if (!ladish_app_supervisor_start_app((ladish_app_supervisor_handle)supervisor_ptr, (ladish_app_handle)app_ptr))
742 log_error("Execution of '%s' failed", app_ptr->commandline);
743 return;
748 void ladish_app_supervisor_stop(ladish_app_supervisor_handle supervisor_handle)
750 struct list_head * node_ptr;
751 struct ladish_app * app_ptr;
753 list_for_each(node_ptr, &supervisor_ptr->applist)
755 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
756 if (app_ptr->pid != 0)
758 log_info("terminating '%s'...", app_ptr->name);
759 ladish_app_send_signal(app_ptr, SIGTERM, false);
760 app_ptr->autorun = true;
761 app_ptr->state = LADISH_APP_STATE_STOPPING;
766 void ladish_app_supervisor_save_L1(ladish_app_supervisor_handle supervisor_handle)
768 struct list_head * node_ptr;
769 struct ladish_app * app_ptr;
771 list_for_each(node_ptr, &supervisor_ptr->applist)
773 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
774 if (app_ptr->state != LADISH_APP_STATE_STARTED)
776 continue;
779 if (app_ptr->pid == 0)
781 ASSERT_NO_PASS;
782 continue;
785 ladish_app_save_L1_internal(app_ptr);
789 const char * ladish_app_supervisor_get_name(ladish_app_supervisor_handle supervisor_handle)
791 return supervisor_ptr->name;
794 unsigned int ladish_app_supervisor_get_running_app_count(ladish_app_supervisor_handle supervisor_handle)
796 struct list_head * node_ptr;
797 struct ladish_app * app_ptr;
798 unsigned int counter;
800 counter = 0;
801 list_for_each(node_ptr, &supervisor_ptr->applist)
803 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
804 if (app_ptr->pid != 0)
806 counter++;
810 return counter;
813 bool ladish_app_supervisor_has_apps(ladish_app_supervisor_handle supervisor_handle)
815 return !list_empty(&supervisor_ptr->applist);
818 #undef supervisor_ptr
820 /**********************************************************************************/
821 /* D-Bus methods */
822 /**********************************************************************************/
824 #define supervisor_ptr ((struct ladish_app_supervisor *)call_ptr->iface_context)
826 static void get_all(struct dbus_method_call * call_ptr)
828 DBusMessageIter iter, array_iter, struct_iter;
829 struct list_head * node_ptr;
830 struct ladish_app * app_ptr;
831 dbus_bool_t running;
832 dbus_bool_t terminal;
834 //log_info("get_all called");
836 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
837 if (call_ptr->reply == NULL)
839 goto fail;
842 dbus_message_iter_init_append(call_ptr->reply, &iter);
844 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, &supervisor_ptr->version))
846 goto fail_unref;
849 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(tsbby)", &array_iter))
851 goto fail_unref;
854 list_for_each(node_ptr, &supervisor_ptr->applist)
856 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
858 log_info("app '%s' (%llu)", app_ptr->name, (unsigned long long)app_ptr->id);
860 if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
862 goto fail_unref;
865 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &app_ptr->id))
867 goto fail_unref;
870 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &app_ptr->name))
872 goto fail_unref;
875 running = app_ptr->pid != 0;
876 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BOOLEAN, &running))
878 goto fail_unref;
881 terminal = app_ptr->terminal;
882 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BOOLEAN, &terminal))
884 goto fail_unref;
887 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &app_ptr->level))
889 goto fail_unref;
892 if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
894 goto fail_unref;
898 if (!dbus_message_iter_close_container(&iter, &array_iter))
900 goto fail_unref;
903 return;
905 fail_unref:
906 dbus_message_unref(call_ptr->reply);
907 call_ptr->reply = NULL;
909 fail:
910 log_error("Ran out of memory trying to construct method return");
913 static void run_custom(struct dbus_method_call * call_ptr)
915 dbus_bool_t terminal;
916 const char * commandline;
917 const char * name;
918 uint8_t level;
920 if (!dbus_message_get_args(
921 call_ptr->message,
922 &g_dbus_error,
923 DBUS_TYPE_BOOLEAN, &terminal,
924 DBUS_TYPE_STRING, &commandline,
925 DBUS_TYPE_STRING, &name,
926 DBUS_TYPE_BYTE, &level,
927 DBUS_TYPE_INVALID))
929 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
930 dbus_error_free(&g_dbus_error);
931 return;
934 log_info("run_custom('%s', %s, '%s', %"PRIu8") called", name, terminal ? "terminal" : "shell", commandline, level);
936 if (level != 0 && level != 1)
938 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "invalid level %"PRIu8, level);
939 return;
942 if (ladish_command_new_app(
943 call_ptr,
944 ladish_studio_get_cmd_queue(),
945 supervisor_ptr->opath,
946 terminal,
947 commandline,
948 name,
949 level))
951 method_return_new_void(call_ptr);
955 static void start_app(struct dbus_method_call * call_ptr)
957 uint64_t id;
959 if (!dbus_message_get_args(
960 call_ptr->message,
961 &g_dbus_error,
962 DBUS_TYPE_UINT64, &id,
963 DBUS_TYPE_INVALID))
965 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
966 dbus_error_free(&g_dbus_error);
967 return;
970 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_STARTED))
972 method_return_new_void(call_ptr);
976 static void stop_app(struct dbus_method_call * call_ptr)
978 uint64_t id;
980 if (!dbus_message_get_args(
981 call_ptr->message,
982 &g_dbus_error,
983 DBUS_TYPE_UINT64, &id,
984 DBUS_TYPE_INVALID))
986 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
987 dbus_error_free(&g_dbus_error);
988 return;
991 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_STOPPED))
993 method_return_new_void(call_ptr);
997 static void kill_app(struct dbus_method_call * call_ptr)
999 uint64_t id;
1001 if (!dbus_message_get_args(
1002 call_ptr->message,
1003 &g_dbus_error,
1004 DBUS_TYPE_UINT64, &id,
1005 DBUS_TYPE_INVALID))
1007 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1008 dbus_error_free(&g_dbus_error);
1009 return;
1012 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_KILL))
1014 method_return_new_void(call_ptr);
1018 static void get_app_properties(struct dbus_method_call * call_ptr)
1020 uint64_t id;
1021 struct ladish_app * app_ptr;
1022 dbus_bool_t running;
1023 dbus_bool_t terminal;
1025 if (!dbus_message_get_args(
1026 call_ptr->message,
1027 &g_dbus_error,
1028 DBUS_TYPE_UINT64, &id,
1029 DBUS_TYPE_INVALID))
1031 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1032 dbus_error_free(&g_dbus_error);
1033 return;
1036 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
1037 if (app_ptr == NULL)
1039 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
1040 return;
1043 running = app_ptr->pid != 0;
1044 terminal = app_ptr->terminal;
1046 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
1047 if (call_ptr->reply == NULL)
1049 goto fail;
1052 if (!dbus_message_append_args(
1053 call_ptr->reply,
1054 DBUS_TYPE_STRING, &app_ptr->name,
1055 DBUS_TYPE_STRING, &app_ptr->commandline,
1056 DBUS_TYPE_BOOLEAN, &running,
1057 DBUS_TYPE_BOOLEAN, &terminal,
1058 DBUS_TYPE_BYTE, &app_ptr->level,
1059 DBUS_TYPE_INVALID))
1061 goto fail_unref;
1064 return;
1066 fail_unref:
1067 dbus_message_unref(call_ptr->reply);
1068 call_ptr->reply = NULL;
1070 fail:
1071 log_error("Ran out of memory trying to construct method return");
1074 static void set_app_properties(struct dbus_method_call * call_ptr)
1076 uint64_t id;
1077 dbus_bool_t terminal;
1078 const char * name;
1079 const char * commandline;
1080 uint8_t level;
1081 struct ladish_app * app_ptr;
1082 char * name_buffer;
1083 char * commandline_buffer;
1085 if (!dbus_message_get_args(
1086 call_ptr->message,
1087 &g_dbus_error,
1088 DBUS_TYPE_UINT64, &id,
1089 DBUS_TYPE_STRING, &name,
1090 DBUS_TYPE_STRING, &commandline,
1091 DBUS_TYPE_BOOLEAN, &terminal,
1092 DBUS_TYPE_BYTE, &level,
1093 DBUS_TYPE_INVALID))
1095 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1096 dbus_error_free(&g_dbus_error);
1097 return;
1100 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
1101 if (app_ptr == NULL)
1103 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
1104 return;
1107 if (app_ptr->pid != 0 && strcmp(commandline, app_ptr->commandline) != 0)
1109 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot change commandline when app is running. '%s' -> '%s'", app_ptr->commandline, commandline);
1110 return;
1113 if (app_ptr->pid != 0 && ((app_ptr->terminal && !terminal) || (!app_ptr->terminal && terminal)))
1115 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot change whether to run in terminal when app is running");
1116 return;
1119 if (app_ptr->pid != 0 && app_ptr->level != level)
1121 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot change app level when app is running");
1122 return;
1125 if (strcmp(commandline, app_ptr->commandline) != 0)
1127 commandline_buffer = strdup(commandline);
1128 if (commandline_buffer == NULL)
1130 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed for app commandline");
1131 return;
1134 else
1136 commandline_buffer = NULL;
1139 if (strcmp(name, app_ptr->name) != 0)
1141 name_buffer = strdup(name);
1142 if (name_buffer == NULL)
1144 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed for app nam");
1145 if (commandline_buffer != NULL)
1147 free(commandline_buffer);
1149 return;
1152 else
1154 name_buffer = NULL;
1157 if (name_buffer != NULL)
1159 supervisor_ptr->on_app_renamed(supervisor_ptr->on_app_renamed_context, app_ptr->uuid, app_ptr->name, name_buffer);
1160 free(app_ptr->name);
1161 app_ptr->name = name_buffer;
1164 if (commandline_buffer != NULL)
1166 free(app_ptr->commandline);
1167 app_ptr->commandline = commandline_buffer;
1170 app_ptr->level = level;
1171 app_ptr->terminal = terminal;
1173 emit_app_state_changed(supervisor_ptr, app_ptr);
1175 method_return_new_void(call_ptr);
1178 static void remove_app(struct dbus_method_call * call_ptr)
1180 uint64_t id;
1182 if (!dbus_message_get_args(
1183 call_ptr->message,
1184 &g_dbus_error,
1185 DBUS_TYPE_UINT64, &id,
1186 DBUS_TYPE_INVALID))
1188 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1189 dbus_error_free(&g_dbus_error);
1190 return;
1193 if (ladish_command_remove_app(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id))
1195 method_return_new_void(call_ptr);
1199 static void is_app_running(struct dbus_method_call * call_ptr)
1201 uint64_t id;
1202 struct ladish_app * app_ptr;
1203 dbus_bool_t running;
1205 if (!dbus_message_get_args(
1206 call_ptr->message,
1207 &g_dbus_error,
1208 DBUS_TYPE_UINT64, &id,
1209 DBUS_TYPE_INVALID))
1211 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1212 dbus_error_free(&g_dbus_error);
1213 return;
1216 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
1217 if (app_ptr == NULL)
1219 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
1220 return;
1223 running = app_ptr->pid != 0;
1225 method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &running);
1228 #undef supervisor_ptr
1230 METHOD_ARGS_BEGIN(GetAll, "Get list of apps")
1231 METHOD_ARG_DESCRIBE_OUT("list_version", DBUS_TYPE_UINT64_AS_STRING, "Version of the list")
1232 METHOD_ARG_DESCRIBE_OUT("apps_list", "a(tsbby)", "List of apps")
1233 METHOD_ARGS_END
1235 METHOD_ARGS_BEGIN(RunCustom, "Start application by supplying commandline")
1236 METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1237 METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1238 METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "Name")
1239 METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1240 METHOD_ARGS_END
1242 METHOD_ARGS_BEGIN(StartApp, "Start application")
1243 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1244 METHOD_ARGS_END
1246 METHOD_ARGS_BEGIN(StopApp, "Stop application")
1247 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1248 METHOD_ARGS_END
1250 METHOD_ARGS_BEGIN(KillApp, "Kill application")
1251 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1252 METHOD_ARGS_END
1254 METHOD_ARGS_BEGIN(RemoveApp, "Remove application")
1255 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1256 METHOD_ARGS_END
1258 METHOD_ARGS_BEGIN(GetAppProperties, "Get properties of an application")
1259 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1260 METHOD_ARG_DESCRIBE_OUT("name", DBUS_TYPE_STRING_AS_STRING, "")
1261 METHOD_ARG_DESCRIBE_OUT("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1262 METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1263 METHOD_ARG_DESCRIBE_OUT("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1264 METHOD_ARG_DESCRIBE_OUT("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1265 METHOD_ARGS_END
1267 METHOD_ARGS_BEGIN(SetAppProperties, "Set properties of an application")
1268 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1269 METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "")
1270 METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1271 METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1272 METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1273 METHOD_ARGS_END
1275 METHOD_ARGS_BEGIN(IsAppRunning, "Check whether application is running")
1276 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1277 METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether app is running")
1278 METHOD_ARGS_END
1281 METHODS_BEGIN
1282 METHOD_DESCRIBE(GetAll, get_all) /* sync */
1283 METHOD_DESCRIBE(RunCustom, run_custom) /* async */
1284 METHOD_DESCRIBE(StartApp, start_app) /* async */
1285 METHOD_DESCRIBE(StopApp, stop_app) /* async */
1286 METHOD_DESCRIBE(KillApp, kill_app) /* async */
1287 METHOD_DESCRIBE(GetAppProperties, get_app_properties) /* sync */
1288 METHOD_DESCRIBE(SetAppProperties, set_app_properties) /* sync */
1289 METHOD_DESCRIBE(RemoveApp, remove_app) /* sync */
1290 METHOD_DESCRIBE(IsAppRunning, is_app_running) /* sync */
1291 METHODS_END
1293 SIGNAL_ARGS_BEGIN(AppAdded, "")
1294 SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1295 SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1296 SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
1297 SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1298 SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1299 SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1300 SIGNAL_ARGS_END
1302 SIGNAL_ARGS_BEGIN(AppRemoved, "")
1303 SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1304 SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1305 SIGNAL_ARGS_END
1307 SIGNAL_ARGS_BEGIN(AppStateChanged, "")
1308 SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1309 SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1310 SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
1311 SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1312 SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1313 SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1314 SIGNAL_ARGS_END
1316 SIGNALS_BEGIN
1317 SIGNAL_DESCRIBE(AppAdded)
1318 SIGNAL_DESCRIBE(AppRemoved)
1319 SIGNAL_DESCRIBE(AppStateChanged)
1320 SIGNALS_END
1322 INTERFACE_BEGIN(g_iface_app_supervisor, IFACE_APP_SUPERVISOR)
1323 INTERFACE_DEFAULT_HANDLER
1324 INTERFACE_EXPOSE_METHODS
1325 INTERFACE_EXPOSE_SIGNALS
1326 INTERFACE_END