ladishd: clear project even if room project state is 'unloaded'. Fix for #117
[ladish.git] / daemon / app_supervisor.c
blobdb1bacf81cab3aaad0eeeb57f8ca2164b35c3f8d
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>
32 #include "app_supervisor.h"
33 #include "../dbus/error.h"
34 #include "../dbus_constants.h"
35 #include "loader.h"
36 #include "studio_internal.h"
37 #include "../proxies/notify_proxy.h"
39 struct ladish_app
41 struct list_head siblings;
42 uint64_t id;
43 uuid_t uuid;
44 char * name;
45 char * commandline;
46 bool terminal;
47 uint8_t level;
48 pid_t pid;
49 pid_t firstborn_pid;
50 bool zombie; /* if true, remove when stopped */
51 bool autorun;
52 unsigned int state;
55 struct ladish_app_supervisor
57 char * name;
58 char * opath;
59 char * dir;
60 uint64_t version;
61 uint64_t next_id;
62 struct list_head applist;
63 void * on_app_renamed_context;
64 ladish_app_supervisor_on_app_renamed_callback on_app_renamed;
67 bool
68 ladish_app_supervisor_create(
69 ladish_app_supervisor_handle * supervisor_handle_ptr,
70 const char * opath,
71 const char * name,
72 void * context,
73 ladish_app_supervisor_on_app_renamed_callback on_app_renamed)
75 struct ladish_app_supervisor * supervisor_ptr;
77 supervisor_ptr = malloc(sizeof(struct ladish_app_supervisor));
78 if (supervisor_ptr == NULL)
80 log_error("malloc() failed to allocate struct ladish_app_supervisor");
81 return false;
84 supervisor_ptr->opath = strdup(opath);
85 if (supervisor_ptr->opath == NULL)
87 log_error("strdup() failed for app supervisor opath");
88 free(supervisor_ptr);
89 return false;
92 supervisor_ptr->name = strdup(name);
93 if (supervisor_ptr->name == NULL)
95 log_error("strdup() failed for app supervisor name");
96 free(supervisor_ptr->opath);
97 free(supervisor_ptr);
98 return false;
101 supervisor_ptr->dir = NULL;
103 supervisor_ptr->version = 0;
104 supervisor_ptr->next_id = 1;
106 INIT_LIST_HEAD(&supervisor_ptr->applist);
108 supervisor_ptr->on_app_renamed_context = context;
109 supervisor_ptr->on_app_renamed = on_app_renamed;
111 *supervisor_handle_ptr = (ladish_app_supervisor_handle)supervisor_ptr;
113 return true;
116 struct ladish_app * ladish_app_supervisor_find_app_by_id_internal(struct ladish_app_supervisor * supervisor_ptr, uint64_t id)
118 struct list_head * node_ptr;
119 struct ladish_app * app_ptr;
121 list_for_each(node_ptr, &supervisor_ptr->applist)
123 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
124 if (app_ptr->id == id)
126 return app_ptr;
130 return NULL;
133 void remove_app_internal(struct ladish_app_supervisor * supervisor_ptr, struct ladish_app * app_ptr)
135 ASSERT(app_ptr->pid == 0); /* Removing not-stoped app? Zombies will make a rebellion! */
137 list_del(&app_ptr->siblings);
139 supervisor_ptr->version++;
141 dbus_signal_emit(
142 g_dbus_connection,
143 supervisor_ptr->opath,
144 IFACE_APP_SUPERVISOR,
145 "AppRemoved",
146 "tt",
147 &supervisor_ptr->version,
148 &app_ptr->id);
150 free(app_ptr->name);
151 free(app_ptr->commandline);
152 free(app_ptr);
155 void emit_app_state_changed(struct ladish_app_supervisor * supervisor_ptr, struct ladish_app * app_ptr)
157 dbus_bool_t running;
158 dbus_bool_t terminal;
160 running = app_ptr->pid != 0;
161 terminal = app_ptr->terminal;
163 supervisor_ptr->version++;
165 dbus_signal_emit(
166 g_dbus_connection,
167 supervisor_ptr->opath,
168 IFACE_APP_SUPERVISOR,
169 "AppStateChanged",
170 "ttsbby",
171 &supervisor_ptr->version,
172 &app_ptr->id,
173 &app_ptr->name,
174 &running,
175 &terminal,
176 &app_ptr->level);
179 #define supervisor_ptr ((struct ladish_app_supervisor *)supervisor_handle)
181 const char * ladish_app_supervisor_get_opath(ladish_app_supervisor_handle supervisor_handle)
183 return supervisor_ptr->opath;
186 ladish_app_handle ladish_app_supervisor_find_app_by_name(ladish_app_supervisor_handle supervisor_handle, const char * name)
188 struct list_head * node_ptr;
189 struct ladish_app * app_ptr;
191 list_for_each(node_ptr, &supervisor_ptr->applist)
193 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
194 if (strcmp(app_ptr->name, name) == 0)
196 return (ladish_app_handle)app_ptr;
200 return NULL;
203 ladish_app_handle ladish_app_supervisor_find_app_by_id(ladish_app_supervisor_handle supervisor_handle, uint64_t id)
205 return (ladish_app_handle)ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
208 ladish_app_handle ladish_app_supervisor_find_app_by_pid(ladish_app_supervisor_handle supervisor_handle, pid_t pid)
210 struct list_head * node_ptr;
211 struct ladish_app * app_ptr;
213 list_for_each(node_ptr, &supervisor_ptr->applist)
215 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
216 if (app_ptr->pid == pid)
218 //log_info("app \"%s\" found by pid %llu", app_ptr->name, (unsigned long long)pid);
219 return (ladish_app_handle)app_ptr;
223 return NULL;
226 ladish_app_handle
227 ladish_app_supervisor_add(
228 ladish_app_supervisor_handle supervisor_handle,
229 const char * name,
230 bool autorun,
231 const char * command,
232 bool terminal,
233 uint8_t level)
235 struct ladish_app * app_ptr;
236 dbus_bool_t running;
238 app_ptr = malloc(sizeof(struct ladish_app));
239 if (app_ptr == NULL)
241 log_error("malloc of struct ladish_app failed");
242 return NULL;
245 app_ptr->name = strdup(name);
246 if (app_ptr->name == NULL)
248 log_error("strdup() failed for app name");
249 free(app_ptr);
250 return NULL;
253 app_ptr->commandline = strdup(command);
254 if (app_ptr->commandline == NULL)
256 log_error("strdup() failed for app commandline");
257 free(app_ptr->name);
258 free(app_ptr);
259 return NULL;
262 app_ptr->terminal = terminal;
263 app_ptr->level = level;
264 app_ptr->pid = 0;
265 app_ptr->firstborn_pid = 0;
267 app_ptr->id = supervisor_ptr->next_id++;
268 uuid_generate(app_ptr->uuid);
269 app_ptr->zombie = false;
270 app_ptr->state = LADISH_APP_STATE_STOPPED;
271 app_ptr->autorun = autorun;
272 list_add_tail(&app_ptr->siblings, &supervisor_ptr->applist);
274 supervisor_ptr->version++;
276 running = false;
277 dbus_signal_emit(
278 g_dbus_connection,
279 supervisor_ptr->opath,
280 IFACE_APP_SUPERVISOR,
281 "AppAdded",
282 "ttsbby",
283 &supervisor_ptr->version,
284 &app_ptr->id,
285 &app_ptr->name,
286 &running,
287 &terminal,
288 &app_ptr->level);
290 return (ladish_app_handle)app_ptr;
293 static void ladish_app_send_signal(struct ladish_app * app_ptr, int sig, bool prefer_firstborn)
295 pid_t pid;
296 const char * signal_name;
298 ASSERT(app_ptr->state = LADISH_APP_STATE_STARTED);
299 if (app_ptr->pid <= 0)
301 ASSERT_NO_PASS;
302 return;
305 if (prefer_firstborn && app_ptr->firstborn_pid != 0)
307 pid = app_ptr->firstborn_pid;
309 else
311 pid = app_ptr->pid;
314 switch (sig)
316 case SIGTERM:
317 signal_name = "SIGTERM";
318 break;
319 case SIGKILL:
320 signal_name = "SIGKILL";
321 break;
322 case SIGUSR1:
323 signal_name = "SIGUSR1";
324 break;
325 default:
326 signal_name = strsignal(sig);
327 if (signal_name == NULL)
329 signal_name = "unknown";
333 log_info("sending signal %d (%s) to '%s' with pid %u", sig, signal_name, app_ptr->name, (unsigned int)pid);
335 kill(pid, sig);
338 bool ladish_app_supervisor_clear(ladish_app_supervisor_handle supervisor_handle)
340 struct list_head * node_ptr;
341 struct list_head * safe_node_ptr;
342 struct ladish_app * app_ptr;
343 bool lifeless;
345 if (supervisor_ptr->dir != NULL)
347 free(supervisor_ptr->dir);
348 supervisor_ptr->dir = NULL;
351 lifeless = true;
353 list_for_each_safe(node_ptr, safe_node_ptr, &supervisor_ptr->applist)
355 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
356 if (app_ptr->pid != 0)
358 log_info("terminating '%s'...", app_ptr->name);
359 ladish_app_send_signal(app_ptr, SIGTERM, false);
360 app_ptr->zombie = true;
361 app_ptr->state = LADISH_APP_STATE_STOPPING;
362 lifeless = false;
364 else
366 log_info("removing '%s'", app_ptr->name);
367 remove_app_internal(supervisor_ptr, app_ptr);
371 return lifeless;
374 void ladish_app_supervisor_destroy(ladish_app_supervisor_handle supervisor_handle)
376 ladish_app_supervisor_clear(supervisor_handle);
377 free(supervisor_ptr->name);
378 free(supervisor_ptr->opath);
379 free(supervisor_ptr);
382 bool
383 ladish_app_supervisor_set_directory(
384 ladish_app_supervisor_handle supervisor_handle,
385 const char * dir)
387 char * dup;
389 dup = strdup(dir);
390 if (dup == NULL)
392 log_error("strdup(\"%s\") failed", dir);
393 return false;
396 if (supervisor_ptr->dir != NULL)
398 free(supervisor_ptr->dir);
401 supervisor_ptr->dir = dup;
403 return true;
406 bool ladish_app_supervisor_child_exit(ladish_app_supervisor_handle supervisor_handle, pid_t pid)
408 struct list_head * node_ptr;
409 struct ladish_app * app_ptr;
411 list_for_each(node_ptr, &supervisor_ptr->applist)
413 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
414 if (app_ptr->pid == pid)
416 log_info("exit of child '%s' detected.", app_ptr->name);
418 app_ptr->pid = 0;
419 app_ptr->firstborn_pid = 0;
420 if (app_ptr->zombie)
422 remove_app_internal(supervisor_ptr, app_ptr);
424 else
426 if (app_ptr->state == LADISH_APP_STATE_STARTED)
428 ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "App terminated unexpectedly", app_ptr->name);
431 app_ptr->state = LADISH_APP_STATE_STOPPED;
433 emit_app_state_changed(supervisor_ptr, app_ptr);
436 return true;
440 return false;
443 bool
444 ladish_app_supervisor_enum(
445 ladish_app_supervisor_handle supervisor_handle,
446 void * context,
447 ladish_app_supervisor_enum_callback callback)
449 struct list_head * node_ptr;
450 struct ladish_app * app_ptr;
452 list_for_each(node_ptr, &supervisor_ptr->applist)
454 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
456 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))
458 return false;
462 return true;
465 static inline void ladish_app_save_L1_internal(struct ladish_app * app_ptr)
467 if (app_ptr->level == 1)
469 ladish_app_send_signal(app_ptr, SIGUSR1, true);
473 #define app_ptr ((struct ladish_app *)app_handle)
475 bool ladish_app_supervisor_start_app(ladish_app_supervisor_handle supervisor_handle, ladish_app_handle app_handle)
477 app_ptr->zombie = false;
479 ASSERT(app_ptr->pid == 0);
481 if (!loader_execute(
482 supervisor_ptr->name,
483 app_ptr->name,
484 supervisor_ptr->dir != NULL ? supervisor_ptr->dir : "/",
485 app_ptr->terminal,
486 app_ptr->commandline,
487 &app_ptr->pid))
489 return false;
492 ASSERT(app_ptr->pid != 0);
493 app_ptr->state = LADISH_APP_STATE_STARTED;
495 emit_app_state_changed(supervisor_ptr, app_ptr);
496 return true;
499 void ladish_app_supervisor_remove_app(ladish_app_supervisor_handle supervisor_handle, ladish_app_handle app_handle)
501 remove_app_internal(supervisor_ptr, app_ptr);
504 unsigned int ladish_app_get_state(ladish_app_handle app_handle)
506 return app_ptr->state;
509 bool ladish_app_is_running(ladish_app_handle app_handle)
511 return app_ptr->pid != 0;
514 const char * ladish_app_get_name(ladish_app_handle app_handle)
516 return app_ptr->name;
519 void ladish_app_get_uuid(ladish_app_handle app_handle, uuid_t uuid)
521 uuid_copy(uuid, app_ptr->uuid);
524 void ladish_app_stop(ladish_app_handle app_handle)
526 ladish_app_send_signal(app_ptr, SIGTERM, false);
527 app_ptr->state = LADISH_APP_STATE_STOPPING;
530 void ladish_app_kill(ladish_app_handle app_handle)
532 ladish_app_send_signal(app_ptr, SIGKILL, false);
533 app_ptr->state = LADISH_APP_STATE_KILL;
536 void ladish_app_save_L1(ladish_app_handle app_handle)
538 ladish_app_save_L1_internal(app_ptr);
541 void ladish_app_add_pid(ladish_app_handle app_handle, pid_t pid)
543 if (app_ptr->pid == 0)
545 log_error("Associating pid with stopped app does not make sense");
546 ASSERT_NO_PASS;
547 return;
550 if (pid <= 1) /* catch -1, 0 and 1 */
552 log_error("Refusing domination by ignoring pid %d", (int)pid);
553 ASSERT_NO_PASS;
554 return;
557 if (app_ptr->pid == pid)
558 { /* The top level process that is already known */
559 return;
562 if (app_ptr->firstborn_pid != 0)
563 { /* Ignore non-first children */
564 return;
567 log_info("First grandchild with pid %u", (unsigned int)pid);
568 app_ptr->firstborn_pid = pid;
571 #undef app_ptr
573 void ladish_app_supervisor_autorun(ladish_app_supervisor_handle supervisor_handle)
575 struct list_head * node_ptr;
576 struct ladish_app * app_ptr;
578 list_for_each(node_ptr, &supervisor_ptr->applist)
580 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
582 if (!app_ptr->autorun)
584 continue;
587 app_ptr->autorun = false;
589 log_info("autorun('%s', %s, '%s') called", app_ptr->name, app_ptr->terminal ? "terminal" : "shell", app_ptr->commandline);
591 if (!ladish_app_supervisor_start_app((ladish_app_supervisor_handle)supervisor_ptr, (ladish_app_handle)app_ptr))
593 log_error("Execution of '%s' failed", app_ptr->commandline);
594 return;
599 void ladish_app_supervisor_stop(ladish_app_supervisor_handle supervisor_handle)
601 struct list_head * node_ptr;
602 struct ladish_app * app_ptr;
604 list_for_each(node_ptr, &supervisor_ptr->applist)
606 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
607 if (app_ptr->pid != 0)
609 log_info("terminating '%s'...", app_ptr->name);
610 ladish_app_send_signal(app_ptr, SIGTERM, false);
611 app_ptr->autorun = true;
612 app_ptr->state = LADISH_APP_STATE_STOPPING;
617 void ladish_app_supervisor_save_L1(ladish_app_supervisor_handle supervisor_handle)
619 struct list_head * node_ptr;
620 struct ladish_app * app_ptr;
622 list_for_each(node_ptr, &supervisor_ptr->applist)
624 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
625 if (app_ptr->state != LADISH_APP_STATE_STARTED)
627 continue;
630 if (app_ptr->pid == 0)
632 ASSERT_NO_PASS;
633 continue;
636 ladish_app_save_L1_internal(app_ptr);
640 const char * ladish_app_supervisor_get_name(ladish_app_supervisor_handle supervisor_handle)
642 return supervisor_ptr->name;
645 unsigned int ladish_app_supervisor_get_running_app_count(ladish_app_supervisor_handle supervisor_handle)
647 struct list_head * node_ptr;
648 struct ladish_app * app_ptr;
649 unsigned int counter;
651 counter = 0;
652 list_for_each(node_ptr, &supervisor_ptr->applist)
654 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
655 if (app_ptr->pid != 0)
657 counter++;
661 return counter;
664 bool ladish_app_supervisor_has_apps(ladish_app_supervisor_handle supervisor_handle)
666 return !list_empty(&supervisor_ptr->applist);
669 #undef supervisor_ptr
671 /**********************************************************************************/
672 /* D-Bus methods */
673 /**********************************************************************************/
675 #define supervisor_ptr ((struct ladish_app_supervisor *)call_ptr->iface_context)
677 static void get_all(struct dbus_method_call * call_ptr)
679 DBusMessageIter iter, array_iter, struct_iter;
680 struct list_head * node_ptr;
681 struct ladish_app * app_ptr;
682 dbus_bool_t running;
683 dbus_bool_t terminal;
685 //log_info("get_all called");
687 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
688 if (call_ptr->reply == NULL)
690 goto fail;
693 dbus_message_iter_init_append(call_ptr->reply, &iter);
695 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, &supervisor_ptr->version))
697 goto fail_unref;
700 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(tsbby)", &array_iter))
702 goto fail_unref;
705 list_for_each(node_ptr, &supervisor_ptr->applist)
707 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
709 log_info("app '%s' (%llu)", app_ptr->name, (unsigned long long)app_ptr->id);
711 if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
713 goto fail_unref;
716 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &app_ptr->id))
718 goto fail_unref;
721 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &app_ptr->name))
723 goto fail_unref;
726 running = app_ptr->pid != 0;
727 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BOOLEAN, &running))
729 goto fail_unref;
732 terminal = app_ptr->terminal;
733 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BOOLEAN, &terminal))
735 goto fail_unref;
738 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &app_ptr->level))
740 goto fail_unref;
743 if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
745 goto fail_unref;
749 if (!dbus_message_iter_close_container(&iter, &array_iter))
751 goto fail_unref;
754 return;
756 fail_unref:
757 dbus_message_unref(call_ptr->reply);
758 call_ptr->reply = NULL;
760 fail:
761 log_error("Ran out of memory trying to construct method return");
764 static void run_custom(struct dbus_method_call * call_ptr)
766 dbus_bool_t terminal;
767 const char * commandline;
768 const char * name;
769 uint8_t level;
771 if (!dbus_message_get_args(
772 call_ptr->message,
773 &g_dbus_error,
774 DBUS_TYPE_BOOLEAN, &terminal,
775 DBUS_TYPE_STRING, &commandline,
776 DBUS_TYPE_STRING, &name,
777 DBUS_TYPE_BYTE, &level,
778 DBUS_TYPE_INVALID))
780 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
781 dbus_error_free(&g_dbus_error);
782 return;
785 log_info("run_custom('%s', %s, '%s', %"PRIu8") called", name, terminal ? "terminal" : "shell", commandline, level);
787 if (level != 0 && level != 1)
789 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "invalid level %"PRIu8, level);
790 return;
793 if (ladish_command_new_app(
794 call_ptr,
795 ladish_studio_get_cmd_queue(),
796 supervisor_ptr->opath,
797 terminal,
798 commandline,
799 name,
800 level))
802 method_return_new_void(call_ptr);
806 static void start_app(struct dbus_method_call * call_ptr)
808 uint64_t id;
810 if (!dbus_message_get_args(
811 call_ptr->message,
812 &g_dbus_error,
813 DBUS_TYPE_UINT64, &id,
814 DBUS_TYPE_INVALID))
816 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
817 dbus_error_free(&g_dbus_error);
818 return;
821 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_STARTED))
823 method_return_new_void(call_ptr);
827 static void stop_app(struct dbus_method_call * call_ptr)
829 uint64_t id;
831 if (!dbus_message_get_args(
832 call_ptr->message,
833 &g_dbus_error,
834 DBUS_TYPE_UINT64, &id,
835 DBUS_TYPE_INVALID))
837 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
838 dbus_error_free(&g_dbus_error);
839 return;
842 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_STOPPED))
844 method_return_new_void(call_ptr);
848 static void kill_app(struct dbus_method_call * call_ptr)
850 uint64_t id;
852 if (!dbus_message_get_args(
853 call_ptr->message,
854 &g_dbus_error,
855 DBUS_TYPE_UINT64, &id,
856 DBUS_TYPE_INVALID))
858 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
859 dbus_error_free(&g_dbus_error);
860 return;
863 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_KILL))
865 method_return_new_void(call_ptr);
869 static void get_app_properties(struct dbus_method_call * call_ptr)
871 uint64_t id;
872 struct ladish_app * app_ptr;
873 dbus_bool_t running;
874 dbus_bool_t terminal;
876 if (!dbus_message_get_args(
877 call_ptr->message,
878 &g_dbus_error,
879 DBUS_TYPE_UINT64, &id,
880 DBUS_TYPE_INVALID))
882 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
883 dbus_error_free(&g_dbus_error);
884 return;
887 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
888 if (app_ptr == NULL)
890 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
891 return;
894 running = app_ptr->pid != 0;
895 terminal = app_ptr->terminal;
897 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
898 if (call_ptr->reply == NULL)
900 goto fail;
903 if (!dbus_message_append_args(
904 call_ptr->reply,
905 DBUS_TYPE_STRING, &app_ptr->name,
906 DBUS_TYPE_STRING, &app_ptr->commandline,
907 DBUS_TYPE_BOOLEAN, &running,
908 DBUS_TYPE_BOOLEAN, &terminal,
909 DBUS_TYPE_BYTE, &app_ptr->level,
910 DBUS_TYPE_INVALID))
912 goto fail_unref;
915 return;
917 fail_unref:
918 dbus_message_unref(call_ptr->reply);
919 call_ptr->reply = NULL;
921 fail:
922 log_error("Ran out of memory trying to construct method return");
925 static void set_app_properties(struct dbus_method_call * call_ptr)
927 uint64_t id;
928 dbus_bool_t terminal;
929 const char * name;
930 const char * commandline;
931 uint8_t level;
932 struct ladish_app * app_ptr;
933 char * name_buffer;
934 char * commandline_buffer;
936 if (!dbus_message_get_args(
937 call_ptr->message,
938 &g_dbus_error,
939 DBUS_TYPE_UINT64, &id,
940 DBUS_TYPE_STRING, &name,
941 DBUS_TYPE_STRING, &commandline,
942 DBUS_TYPE_BOOLEAN, &terminal,
943 DBUS_TYPE_BYTE, &level,
944 DBUS_TYPE_INVALID))
946 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
947 dbus_error_free(&g_dbus_error);
948 return;
951 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
952 if (app_ptr == NULL)
954 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
955 return;
958 if (app_ptr->pid != 0 && strcmp(commandline, app_ptr->commandline) != 0)
960 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot change commandline when app is running. '%s' -> '%s'", app_ptr->commandline, commandline);
961 return;
964 if (app_ptr->pid != 0 && ((app_ptr->terminal && !terminal) || (!app_ptr->terminal && terminal)))
966 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot change whether to run in terminal when app is running");
967 return;
970 if (app_ptr->pid != 0 && app_ptr->level != level)
972 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot change app level when app is running");
973 return;
976 if (strcmp(commandline, app_ptr->commandline) != 0)
978 commandline_buffer = strdup(commandline);
979 if (commandline_buffer == NULL)
981 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed for app commandline");
982 return;
985 else
987 commandline_buffer = NULL;
990 if (strcmp(name, app_ptr->name) != 0)
992 name_buffer = strdup(name);
993 if (name_buffer == NULL)
995 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed for app nam");
996 if (commandline_buffer != NULL)
998 free(commandline_buffer);
1000 return;
1003 else
1005 name_buffer = NULL;
1008 if (name_buffer != NULL)
1010 supervisor_ptr->on_app_renamed(supervisor_ptr->on_app_renamed_context, app_ptr->uuid, app_ptr->name, name_buffer);
1011 free(app_ptr->name);
1012 app_ptr->name = name_buffer;
1015 if (commandline_buffer != NULL)
1017 free(app_ptr->commandline);
1018 app_ptr->commandline = commandline_buffer;
1021 app_ptr->level = level;
1022 app_ptr->terminal = terminal;
1024 emit_app_state_changed(supervisor_ptr, app_ptr);
1026 method_return_new_void(call_ptr);
1029 static void remove_app(struct dbus_method_call * call_ptr)
1031 uint64_t id;
1033 if (!dbus_message_get_args(
1034 call_ptr->message,
1035 &g_dbus_error,
1036 DBUS_TYPE_UINT64, &id,
1037 DBUS_TYPE_INVALID))
1039 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1040 dbus_error_free(&g_dbus_error);
1041 return;
1044 if (ladish_command_remove_app(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id))
1046 method_return_new_void(call_ptr);
1050 static void is_app_running(struct dbus_method_call * call_ptr)
1052 uint64_t id;
1053 struct ladish_app * app_ptr;
1054 dbus_bool_t running;
1056 if (!dbus_message_get_args(
1057 call_ptr->message,
1058 &g_dbus_error,
1059 DBUS_TYPE_UINT64, &id,
1060 DBUS_TYPE_INVALID))
1062 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1063 dbus_error_free(&g_dbus_error);
1064 return;
1067 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
1068 if (app_ptr == NULL)
1070 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
1071 return;
1074 running = app_ptr->pid != 0;
1076 method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &running);
1079 #undef supervisor_ptr
1081 METHOD_ARGS_BEGIN(GetAll, "Get list of apps")
1082 METHOD_ARG_DESCRIBE_OUT("list_version", DBUS_TYPE_UINT64_AS_STRING, "Version of the list")
1083 METHOD_ARG_DESCRIBE_OUT("apps_list", "a(tsbby)", "List of apps")
1084 METHOD_ARGS_END
1086 METHOD_ARGS_BEGIN(RunCustom, "Start application by supplying commandline")
1087 METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1088 METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1089 METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "Name")
1090 METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1091 METHOD_ARGS_END
1093 METHOD_ARGS_BEGIN(StartApp, "Start application")
1094 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1095 METHOD_ARGS_END
1097 METHOD_ARGS_BEGIN(StopApp, "Stop application")
1098 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1099 METHOD_ARGS_END
1101 METHOD_ARGS_BEGIN(KillApp, "Kill application")
1102 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1103 METHOD_ARGS_END
1105 METHOD_ARGS_BEGIN(RemoveApp, "Remove application")
1106 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1107 METHOD_ARGS_END
1109 METHOD_ARGS_BEGIN(GetAppProperties, "Get properties of an application")
1110 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1111 METHOD_ARG_DESCRIBE_OUT("name", DBUS_TYPE_STRING_AS_STRING, "")
1112 METHOD_ARG_DESCRIBE_OUT("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1113 METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1114 METHOD_ARG_DESCRIBE_OUT("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1115 METHOD_ARG_DESCRIBE_OUT("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1116 METHOD_ARGS_END
1118 METHOD_ARGS_BEGIN(SetAppProperties, "Set properties of an application")
1119 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1120 METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "")
1121 METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1122 METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1123 METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1124 METHOD_ARGS_END
1126 METHOD_ARGS_BEGIN(IsAppRunning, "Check whether application is running")
1127 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1128 METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether app is running")
1129 METHOD_ARGS_END
1132 METHODS_BEGIN
1133 METHOD_DESCRIBE(GetAll, get_all) /* sync */
1134 METHOD_DESCRIBE(RunCustom, run_custom) /* async */
1135 METHOD_DESCRIBE(StartApp, start_app) /* async */
1136 METHOD_DESCRIBE(StopApp, stop_app) /* async */
1137 METHOD_DESCRIBE(KillApp, kill_app) /* async */
1138 METHOD_DESCRIBE(GetAppProperties, get_app_properties) /* sync */
1139 METHOD_DESCRIBE(SetAppProperties, set_app_properties) /* sync */
1140 METHOD_DESCRIBE(RemoveApp, remove_app) /* sync */
1141 METHOD_DESCRIBE(IsAppRunning, is_app_running) /* sync */
1142 METHODS_END
1144 SIGNAL_ARGS_BEGIN(AppAdded, "")
1145 SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1146 SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1147 SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
1148 SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1149 SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1150 SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1151 SIGNAL_ARGS_END
1153 SIGNAL_ARGS_BEGIN(AppRemoved, "")
1154 SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1155 SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1156 SIGNAL_ARGS_END
1158 SIGNAL_ARGS_BEGIN(AppStateChanged, "")
1159 SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1160 SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1161 SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
1162 SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1163 SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1164 SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1165 SIGNAL_ARGS_END
1167 SIGNALS_BEGIN
1168 SIGNAL_DESCRIBE(AppAdded)
1169 SIGNAL_DESCRIBE(AppRemoved)
1170 SIGNAL_DESCRIBE(AppStateChanged)
1171 SIGNALS_END
1173 INTERFACE_BEGIN(g_iface_app_supervisor, IFACE_APP_SUPERVISOR)
1174 INTERFACE_DEFAULT_HANDLER
1175 INTERFACE_EXPOSE_METHODS
1176 INTERFACE_EXPOSE_SIGNALS
1177 INTERFACE_END