ladishd: Deassociate pids of disappearing clients. Fix for #119
[ladish.git] / daemon / app_supervisor.c
blobccee4f40d91ee66f5ab9224053bf9710667f4c97
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 char * project_name;
61 uint64_t version;
62 uint64_t next_id;
63 struct list_head applist;
64 void * on_app_renamed_context;
65 ladish_app_supervisor_on_app_renamed_callback on_app_renamed;
68 bool
69 ladish_app_supervisor_create(
70 ladish_app_supervisor_handle * supervisor_handle_ptr,
71 const char * opath,
72 const char * name,
73 void * context,
74 ladish_app_supervisor_on_app_renamed_callback on_app_renamed)
76 struct ladish_app_supervisor * supervisor_ptr;
78 supervisor_ptr = malloc(sizeof(struct ladish_app_supervisor));
79 if (supervisor_ptr == NULL)
81 log_error("malloc() failed to allocate struct ladish_app_supervisor");
82 return false;
85 supervisor_ptr->opath = strdup(opath);
86 if (supervisor_ptr->opath == NULL)
88 log_error("strdup() failed for app supervisor opath");
89 free(supervisor_ptr);
90 return false;
93 supervisor_ptr->name = strdup(name);
94 if (supervisor_ptr->name == NULL)
96 log_error("strdup() failed for app supervisor name");
97 free(supervisor_ptr->opath);
98 free(supervisor_ptr);
99 return false;
102 supervisor_ptr->dir = NULL;
103 supervisor_ptr->project_name = NULL;
105 supervisor_ptr->version = 0;
106 supervisor_ptr->next_id = 1;
108 INIT_LIST_HEAD(&supervisor_ptr->applist);
110 supervisor_ptr->on_app_renamed_context = context;
111 supervisor_ptr->on_app_renamed = on_app_renamed;
113 *supervisor_handle_ptr = (ladish_app_supervisor_handle)supervisor_ptr;
115 return true;
118 struct ladish_app * ladish_app_supervisor_find_app_by_id_internal(struct ladish_app_supervisor * supervisor_ptr, uint64_t id)
120 struct list_head * node_ptr;
121 struct ladish_app * app_ptr;
123 list_for_each(node_ptr, &supervisor_ptr->applist)
125 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
126 if (app_ptr->id == id)
128 return app_ptr;
132 return NULL;
135 void remove_app_internal(struct ladish_app_supervisor * supervisor_ptr, struct ladish_app * app_ptr)
137 ASSERT(app_ptr->pid == 0); /* Removing not-stoped app? Zombies will make a rebellion! */
139 list_del(&app_ptr->siblings);
141 supervisor_ptr->version++;
143 dbus_signal_emit(
144 g_dbus_connection,
145 supervisor_ptr->opath,
146 IFACE_APP_SUPERVISOR,
147 "AppRemoved",
148 "tt",
149 &supervisor_ptr->version,
150 &app_ptr->id);
152 free(app_ptr->name);
153 free(app_ptr->commandline);
154 free(app_ptr);
157 void emit_app_state_changed(struct ladish_app_supervisor * supervisor_ptr, struct ladish_app * app_ptr)
159 dbus_bool_t running;
160 dbus_bool_t terminal;
162 running = app_ptr->pid != 0;
163 terminal = app_ptr->terminal;
165 supervisor_ptr->version++;
167 dbus_signal_emit(
168 g_dbus_connection,
169 supervisor_ptr->opath,
170 IFACE_APP_SUPERVISOR,
171 "AppStateChanged",
172 "ttsbby",
173 &supervisor_ptr->version,
174 &app_ptr->id,
175 &app_ptr->name,
176 &running,
177 &terminal,
178 &app_ptr->level);
181 #define supervisor_ptr ((struct ladish_app_supervisor *)supervisor_handle)
183 const char * ladish_app_supervisor_get_opath(ladish_app_supervisor_handle supervisor_handle)
185 return supervisor_ptr->opath;
188 ladish_app_handle ladish_app_supervisor_find_app_by_name(ladish_app_supervisor_handle supervisor_handle, const char * name)
190 struct list_head * node_ptr;
191 struct ladish_app * app_ptr;
193 list_for_each(node_ptr, &supervisor_ptr->applist)
195 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
196 if (strcmp(app_ptr->name, name) == 0)
198 return (ladish_app_handle)app_ptr;
202 return NULL;
205 ladish_app_handle ladish_app_supervisor_find_app_by_id(ladish_app_supervisor_handle supervisor_handle, uint64_t id)
207 return (ladish_app_handle)ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
210 ladish_app_handle ladish_app_supervisor_find_app_by_pid(ladish_app_supervisor_handle supervisor_handle, pid_t pid)
212 struct list_head * node_ptr;
213 struct ladish_app * app_ptr;
215 list_for_each(node_ptr, &supervisor_ptr->applist)
217 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
218 if (app_ptr->pid == pid)
220 //log_info("app \"%s\" found by pid %llu", app_ptr->name, (unsigned long long)pid);
221 return (ladish_app_handle)app_ptr;
225 return NULL;
228 ladish_app_handle ladish_app_supervisor_find_app_by_uuid(ladish_app_supervisor_handle supervisor_handle, const uuid_t uuid)
230 struct list_head * node_ptr;
231 struct ladish_app * app_ptr;
233 list_for_each(node_ptr, &supervisor_ptr->applist)
235 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
236 if (uuid_compare(app_ptr->uuid, uuid) == 0)
238 return (ladish_app_handle)app_ptr;
242 return NULL;
245 ladish_app_handle
246 ladish_app_supervisor_add(
247 ladish_app_supervisor_handle supervisor_handle,
248 const char * name,
249 bool autorun,
250 const char * command,
251 bool terminal,
252 uint8_t level)
254 struct ladish_app * app_ptr;
255 dbus_bool_t running;
257 app_ptr = malloc(sizeof(struct ladish_app));
258 if (app_ptr == NULL)
260 log_error("malloc of struct ladish_app failed");
261 return NULL;
264 app_ptr->name = strdup(name);
265 if (app_ptr->name == NULL)
267 log_error("strdup() failed for app name");
268 free(app_ptr);
269 return NULL;
272 app_ptr->commandline = strdup(command);
273 if (app_ptr->commandline == NULL)
275 log_error("strdup() failed for app commandline");
276 free(app_ptr->name);
277 free(app_ptr);
278 return NULL;
281 app_ptr->terminal = terminal;
282 app_ptr->level = level;
283 app_ptr->pid = 0;
284 app_ptr->firstborn_pid = 0;
286 app_ptr->id = supervisor_ptr->next_id++;
287 uuid_generate(app_ptr->uuid);
288 app_ptr->zombie = false;
289 app_ptr->state = LADISH_APP_STATE_STOPPED;
290 app_ptr->autorun = autorun;
291 list_add_tail(&app_ptr->siblings, &supervisor_ptr->applist);
293 supervisor_ptr->version++;
295 running = false;
296 dbus_signal_emit(
297 g_dbus_connection,
298 supervisor_ptr->opath,
299 IFACE_APP_SUPERVISOR,
300 "AppAdded",
301 "ttsbby",
302 &supervisor_ptr->version,
303 &app_ptr->id,
304 &app_ptr->name,
305 &running,
306 &terminal,
307 &app_ptr->level);
309 return (ladish_app_handle)app_ptr;
312 static void ladish_app_send_signal(struct ladish_app * app_ptr, int sig, bool prefer_firstborn)
314 pid_t pid;
315 const char * signal_name;
317 ASSERT(app_ptr->state = LADISH_APP_STATE_STARTED);
318 if (app_ptr->pid <= 0)
320 ASSERT_NO_PASS;
321 return;
324 if (prefer_firstborn && app_ptr->firstborn_pid != 0)
326 pid = app_ptr->firstborn_pid;
328 else
330 pid = app_ptr->pid;
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 log_info("sending signal %d (%s) to '%s' with pid %u", sig, signal_name, app_ptr->name, (unsigned int)pid);
354 kill(pid, sig);
357 bool ladish_app_supervisor_clear(ladish_app_supervisor_handle supervisor_handle)
359 struct list_head * node_ptr;
360 struct list_head * safe_node_ptr;
361 struct ladish_app * app_ptr;
362 bool lifeless;
364 free(supervisor_ptr->dir);
365 supervisor_ptr->dir = NULL;
367 free(supervisor_ptr->project_name);
368 supervisor_ptr->project_name = NULL;
370 lifeless = true;
372 list_for_each_safe(node_ptr, safe_node_ptr, &supervisor_ptr->applist)
374 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
375 if (app_ptr->pid != 0)
377 log_info("terminating '%s'...", app_ptr->name);
378 ladish_app_send_signal(app_ptr, SIGTERM, false);
379 app_ptr->zombie = true;
380 app_ptr->state = LADISH_APP_STATE_STOPPING;
381 lifeless = false;
383 else
385 log_info("removing '%s'", app_ptr->name);
386 remove_app_internal(supervisor_ptr, app_ptr);
390 return lifeless;
393 void ladish_app_supervisor_destroy(ladish_app_supervisor_handle supervisor_handle)
395 ladish_app_supervisor_clear(supervisor_handle);
396 free(supervisor_ptr->name);
397 free(supervisor_ptr->opath);
398 free(supervisor_ptr);
401 bool
402 ladish_app_supervisor_set_directory(
403 ladish_app_supervisor_handle supervisor_handle,
404 const char * dir)
406 char * dup;
408 dup = strdup(dir);
409 if (dup == NULL)
411 log_error("strdup(\"%s\") failed", dir);
412 return false;
415 if (supervisor_ptr->dir != NULL)
417 free(supervisor_ptr->dir);
420 supervisor_ptr->dir = dup;
422 return true;
425 bool
426 ladish_app_supervisor_set_project_name(
427 ladish_app_supervisor_handle supervisor_handle,
428 const char * project_name)
430 char * dup;
432 if (project_name != NULL)
434 dup = strdup(project_name);
435 if (dup == NULL)
437 log_error("strdup(\"%s\") failed", project_name);
438 return false;
441 else
443 dup = NULL;
446 free(supervisor_ptr->project_name);
447 supervisor_ptr->project_name = dup;
449 return true;
452 bool ladish_app_supervisor_child_exit(ladish_app_supervisor_handle supervisor_handle, pid_t pid)
454 struct list_head * node_ptr;
455 struct ladish_app * app_ptr;
457 list_for_each(node_ptr, &supervisor_ptr->applist)
459 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
460 if (app_ptr->pid == pid)
462 log_info("exit of child '%s' detected.", app_ptr->name);
464 app_ptr->pid = 0;
465 app_ptr->firstborn_pid = 0;
466 if (app_ptr->zombie)
468 remove_app_internal(supervisor_ptr, app_ptr);
470 else
472 if (app_ptr->state == LADISH_APP_STATE_STARTED)
474 ladish_notify_simple(LADISH_NOTIFY_URGENCY_HIGH, "App terminated unexpectedly", app_ptr->name);
477 app_ptr->state = LADISH_APP_STATE_STOPPED;
479 emit_app_state_changed(supervisor_ptr, app_ptr);
482 return true;
486 return false;
489 bool
490 ladish_app_supervisor_enum(
491 ladish_app_supervisor_handle supervisor_handle,
492 void * context,
493 ladish_app_supervisor_enum_callback callback)
495 struct list_head * node_ptr;
496 struct ladish_app * app_ptr;
498 list_for_each(node_ptr, &supervisor_ptr->applist)
500 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
502 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))
504 return false;
508 return true;
511 static inline void ladish_app_save_L1_internal(struct ladish_app * app_ptr)
513 if (app_ptr->level == 1)
515 ladish_app_send_signal(app_ptr, SIGUSR1, true);
519 #define app_ptr ((struct ladish_app *)app_handle)
521 bool ladish_app_supervisor_start_app(ladish_app_supervisor_handle supervisor_handle, ladish_app_handle app_handle)
523 app_ptr->zombie = false;
525 ASSERT(app_ptr->pid == 0);
527 if (!loader_execute(
528 supervisor_ptr->name,
529 supervisor_ptr->project_name,
530 app_ptr->name,
531 supervisor_ptr->dir != NULL ? supervisor_ptr->dir : "/",
532 app_ptr->terminal,
533 app_ptr->commandline,
534 &app_ptr->pid))
536 return false;
539 ASSERT(app_ptr->pid != 0);
540 app_ptr->state = LADISH_APP_STATE_STARTED;
542 emit_app_state_changed(supervisor_ptr, app_ptr);
543 return true;
546 void ladish_app_supervisor_remove_app(ladish_app_supervisor_handle supervisor_handle, ladish_app_handle app_handle)
548 remove_app_internal(supervisor_ptr, app_ptr);
551 unsigned int ladish_app_get_state(ladish_app_handle app_handle)
553 return app_ptr->state;
556 bool ladish_app_is_running(ladish_app_handle app_handle)
558 return app_ptr->pid != 0;
561 const char * ladish_app_get_name(ladish_app_handle app_handle)
563 return app_ptr->name;
566 void ladish_app_get_uuid(ladish_app_handle app_handle, uuid_t uuid)
568 uuid_copy(uuid, app_ptr->uuid);
571 void ladish_app_stop(ladish_app_handle app_handle)
573 ladish_app_send_signal(app_ptr, SIGTERM, false);
574 app_ptr->state = LADISH_APP_STATE_STOPPING;
577 void ladish_app_kill(ladish_app_handle app_handle)
579 ladish_app_send_signal(app_ptr, SIGKILL, false);
580 app_ptr->state = LADISH_APP_STATE_KILL;
583 void ladish_app_save_L1(ladish_app_handle app_handle)
585 ladish_app_save_L1_internal(app_ptr);
588 void ladish_app_add_pid(ladish_app_handle app_handle, pid_t pid)
590 if (app_ptr->pid == 0)
592 log_error("Associating pid with stopped app does not make sense");
593 ASSERT_NO_PASS;
594 return;
597 if (pid <= 1) /* catch -1, 0 and 1 */
599 log_error("Refusing domination by ignoring pid %d", (int)pid);
600 ASSERT_NO_PASS;
601 return;
604 if (app_ptr->pid == pid)
605 { /* The top level process that is already known */
606 return;
609 if (app_ptr->firstborn_pid != 0)
610 { /* Ignore non-first children */
611 return;
614 log_info("First grandchild with pid %u", (unsigned int)pid);
615 app_ptr->firstborn_pid = pid;
618 void ladish_app_del_pid(ladish_app_handle app_handle, pid_t pid)
620 if (app_ptr->firstborn_pid != 0 && app_ptr->firstborn_pid == pid)
622 log_info("First grandchild with pid %u has gone", (unsigned int)pid);
623 app_ptr->firstborn_pid = 0;
627 #undef app_ptr
629 void ladish_app_supervisor_autorun(ladish_app_supervisor_handle supervisor_handle)
631 struct list_head * node_ptr;
632 struct ladish_app * app_ptr;
634 list_for_each(node_ptr, &supervisor_ptr->applist)
636 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
638 if (!app_ptr->autorun)
640 continue;
643 app_ptr->autorun = false;
645 log_info("autorun('%s', %s, '%s') called", app_ptr->name, app_ptr->terminal ? "terminal" : "shell", app_ptr->commandline);
647 if (!ladish_app_supervisor_start_app((ladish_app_supervisor_handle)supervisor_ptr, (ladish_app_handle)app_ptr))
649 log_error("Execution of '%s' failed", app_ptr->commandline);
650 return;
655 void ladish_app_supervisor_stop(ladish_app_supervisor_handle supervisor_handle)
657 struct list_head * node_ptr;
658 struct ladish_app * app_ptr;
660 list_for_each(node_ptr, &supervisor_ptr->applist)
662 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
663 if (app_ptr->pid != 0)
665 log_info("terminating '%s'...", app_ptr->name);
666 ladish_app_send_signal(app_ptr, SIGTERM, false);
667 app_ptr->autorun = true;
668 app_ptr->state = LADISH_APP_STATE_STOPPING;
673 void ladish_app_supervisor_save_L1(ladish_app_supervisor_handle supervisor_handle)
675 struct list_head * node_ptr;
676 struct ladish_app * app_ptr;
678 list_for_each(node_ptr, &supervisor_ptr->applist)
680 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
681 if (app_ptr->state != LADISH_APP_STATE_STARTED)
683 continue;
686 if (app_ptr->pid == 0)
688 ASSERT_NO_PASS;
689 continue;
692 ladish_app_save_L1_internal(app_ptr);
696 const char * ladish_app_supervisor_get_name(ladish_app_supervisor_handle supervisor_handle)
698 return supervisor_ptr->name;
701 unsigned int ladish_app_supervisor_get_running_app_count(ladish_app_supervisor_handle supervisor_handle)
703 struct list_head * node_ptr;
704 struct ladish_app * app_ptr;
705 unsigned int counter;
707 counter = 0;
708 list_for_each(node_ptr, &supervisor_ptr->applist)
710 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
711 if (app_ptr->pid != 0)
713 counter++;
717 return counter;
720 bool ladish_app_supervisor_has_apps(ladish_app_supervisor_handle supervisor_handle)
722 return !list_empty(&supervisor_ptr->applist);
725 #undef supervisor_ptr
727 /**********************************************************************************/
728 /* D-Bus methods */
729 /**********************************************************************************/
731 #define supervisor_ptr ((struct ladish_app_supervisor *)call_ptr->iface_context)
733 static void get_all(struct dbus_method_call * call_ptr)
735 DBusMessageIter iter, array_iter, struct_iter;
736 struct list_head * node_ptr;
737 struct ladish_app * app_ptr;
738 dbus_bool_t running;
739 dbus_bool_t terminal;
741 //log_info("get_all called");
743 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
744 if (call_ptr->reply == NULL)
746 goto fail;
749 dbus_message_iter_init_append(call_ptr->reply, &iter);
751 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, &supervisor_ptr->version))
753 goto fail_unref;
756 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(tsbby)", &array_iter))
758 goto fail_unref;
761 list_for_each(node_ptr, &supervisor_ptr->applist)
763 app_ptr = list_entry(node_ptr, struct ladish_app, siblings);
765 log_info("app '%s' (%llu)", app_ptr->name, (unsigned long long)app_ptr->id);
767 if (!dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
769 goto fail_unref;
772 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &app_ptr->id))
774 goto fail_unref;
777 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &app_ptr->name))
779 goto fail_unref;
782 running = app_ptr->pid != 0;
783 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BOOLEAN, &running))
785 goto fail_unref;
788 terminal = app_ptr->terminal;
789 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BOOLEAN, &terminal))
791 goto fail_unref;
794 if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_BYTE, &app_ptr->level))
796 goto fail_unref;
799 if (!dbus_message_iter_close_container(&array_iter, &struct_iter))
801 goto fail_unref;
805 if (!dbus_message_iter_close_container(&iter, &array_iter))
807 goto fail_unref;
810 return;
812 fail_unref:
813 dbus_message_unref(call_ptr->reply);
814 call_ptr->reply = NULL;
816 fail:
817 log_error("Ran out of memory trying to construct method return");
820 static void run_custom(struct dbus_method_call * call_ptr)
822 dbus_bool_t terminal;
823 const char * commandline;
824 const char * name;
825 uint8_t level;
827 if (!dbus_message_get_args(
828 call_ptr->message,
829 &g_dbus_error,
830 DBUS_TYPE_BOOLEAN, &terminal,
831 DBUS_TYPE_STRING, &commandline,
832 DBUS_TYPE_STRING, &name,
833 DBUS_TYPE_BYTE, &level,
834 DBUS_TYPE_INVALID))
836 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
837 dbus_error_free(&g_dbus_error);
838 return;
841 log_info("run_custom('%s', %s, '%s', %"PRIu8") called", name, terminal ? "terminal" : "shell", commandline, level);
843 if (level != 0 && level != 1)
845 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "invalid level %"PRIu8, level);
846 return;
849 if (ladish_command_new_app(
850 call_ptr,
851 ladish_studio_get_cmd_queue(),
852 supervisor_ptr->opath,
853 terminal,
854 commandline,
855 name,
856 level))
858 method_return_new_void(call_ptr);
862 static void start_app(struct dbus_method_call * call_ptr)
864 uint64_t id;
866 if (!dbus_message_get_args(
867 call_ptr->message,
868 &g_dbus_error,
869 DBUS_TYPE_UINT64, &id,
870 DBUS_TYPE_INVALID))
872 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
873 dbus_error_free(&g_dbus_error);
874 return;
877 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_STARTED))
879 method_return_new_void(call_ptr);
883 static void stop_app(struct dbus_method_call * call_ptr)
885 uint64_t id;
887 if (!dbus_message_get_args(
888 call_ptr->message,
889 &g_dbus_error,
890 DBUS_TYPE_UINT64, &id,
891 DBUS_TYPE_INVALID))
893 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
894 dbus_error_free(&g_dbus_error);
895 return;
898 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_STOPPED))
900 method_return_new_void(call_ptr);
904 static void kill_app(struct dbus_method_call * call_ptr)
906 uint64_t id;
908 if (!dbus_message_get_args(
909 call_ptr->message,
910 &g_dbus_error,
911 DBUS_TYPE_UINT64, &id,
912 DBUS_TYPE_INVALID))
914 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
915 dbus_error_free(&g_dbus_error);
916 return;
919 if (ladish_command_change_app_state(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id, LADISH_APP_STATE_KILL))
921 method_return_new_void(call_ptr);
925 static void get_app_properties(struct dbus_method_call * call_ptr)
927 uint64_t id;
928 struct ladish_app * app_ptr;
929 dbus_bool_t running;
930 dbus_bool_t terminal;
932 if (!dbus_message_get_args(
933 call_ptr->message,
934 &g_dbus_error,
935 DBUS_TYPE_UINT64, &id,
936 DBUS_TYPE_INVALID))
938 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
939 dbus_error_free(&g_dbus_error);
940 return;
943 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
944 if (app_ptr == NULL)
946 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
947 return;
950 running = app_ptr->pid != 0;
951 terminal = app_ptr->terminal;
953 call_ptr->reply = dbus_message_new_method_return(call_ptr->message);
954 if (call_ptr->reply == NULL)
956 goto fail;
959 if (!dbus_message_append_args(
960 call_ptr->reply,
961 DBUS_TYPE_STRING, &app_ptr->name,
962 DBUS_TYPE_STRING, &app_ptr->commandline,
963 DBUS_TYPE_BOOLEAN, &running,
964 DBUS_TYPE_BOOLEAN, &terminal,
965 DBUS_TYPE_BYTE, &app_ptr->level,
966 DBUS_TYPE_INVALID))
968 goto fail_unref;
971 return;
973 fail_unref:
974 dbus_message_unref(call_ptr->reply);
975 call_ptr->reply = NULL;
977 fail:
978 log_error("Ran out of memory trying to construct method return");
981 static void set_app_properties(struct dbus_method_call * call_ptr)
983 uint64_t id;
984 dbus_bool_t terminal;
985 const char * name;
986 const char * commandline;
987 uint8_t level;
988 struct ladish_app * app_ptr;
989 char * name_buffer;
990 char * commandline_buffer;
992 if (!dbus_message_get_args(
993 call_ptr->message,
994 &g_dbus_error,
995 DBUS_TYPE_UINT64, &id,
996 DBUS_TYPE_STRING, &name,
997 DBUS_TYPE_STRING, &commandline,
998 DBUS_TYPE_BOOLEAN, &terminal,
999 DBUS_TYPE_BYTE, &level,
1000 DBUS_TYPE_INVALID))
1002 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1003 dbus_error_free(&g_dbus_error);
1004 return;
1007 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
1008 if (app_ptr == NULL)
1010 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
1011 return;
1014 if (app_ptr->pid != 0 && strcmp(commandline, app_ptr->commandline) != 0)
1016 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot change commandline when app is running. '%s' -> '%s'", app_ptr->commandline, commandline);
1017 return;
1020 if (app_ptr->pid != 0 && ((app_ptr->terminal && !terminal) || (!app_ptr->terminal && terminal)))
1022 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot change whether to run in terminal when app is running");
1023 return;
1026 if (app_ptr->pid != 0 && app_ptr->level != level)
1028 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "Cannot change app level when app is running");
1029 return;
1032 if (strcmp(commandline, app_ptr->commandline) != 0)
1034 commandline_buffer = strdup(commandline);
1035 if (commandline_buffer == NULL)
1037 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed for app commandline");
1038 return;
1041 else
1043 commandline_buffer = NULL;
1046 if (strcmp(name, app_ptr->name) != 0)
1048 name_buffer = strdup(name);
1049 if (name_buffer == NULL)
1051 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_GENERIC, "strdup() failed for app nam");
1052 if (commandline_buffer != NULL)
1054 free(commandline_buffer);
1056 return;
1059 else
1061 name_buffer = NULL;
1064 if (name_buffer != NULL)
1066 supervisor_ptr->on_app_renamed(supervisor_ptr->on_app_renamed_context, app_ptr->uuid, app_ptr->name, name_buffer);
1067 free(app_ptr->name);
1068 app_ptr->name = name_buffer;
1071 if (commandline_buffer != NULL)
1073 free(app_ptr->commandline);
1074 app_ptr->commandline = commandline_buffer;
1077 app_ptr->level = level;
1078 app_ptr->terminal = terminal;
1080 emit_app_state_changed(supervisor_ptr, app_ptr);
1082 method_return_new_void(call_ptr);
1085 static void remove_app(struct dbus_method_call * call_ptr)
1087 uint64_t id;
1089 if (!dbus_message_get_args(
1090 call_ptr->message,
1091 &g_dbus_error,
1092 DBUS_TYPE_UINT64, &id,
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 if (ladish_command_remove_app(call_ptr, ladish_studio_get_cmd_queue(), supervisor_ptr->opath, id))
1102 method_return_new_void(call_ptr);
1106 static void is_app_running(struct dbus_method_call * call_ptr)
1108 uint64_t id;
1109 struct ladish_app * app_ptr;
1110 dbus_bool_t running;
1112 if (!dbus_message_get_args(
1113 call_ptr->message,
1114 &g_dbus_error,
1115 DBUS_TYPE_UINT64, &id,
1116 DBUS_TYPE_INVALID))
1118 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message);
1119 dbus_error_free(&g_dbus_error);
1120 return;
1123 app_ptr = ladish_app_supervisor_find_app_by_id_internal(supervisor_ptr, id);
1124 if (app_ptr == NULL)
1126 lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "App with ID %"PRIu64" not found", id);
1127 return;
1130 running = app_ptr->pid != 0;
1132 method_return_new_single(call_ptr, DBUS_TYPE_BOOLEAN, &running);
1135 #undef supervisor_ptr
1137 METHOD_ARGS_BEGIN(GetAll, "Get list of apps")
1138 METHOD_ARG_DESCRIBE_OUT("list_version", DBUS_TYPE_UINT64_AS_STRING, "Version of the list")
1139 METHOD_ARG_DESCRIBE_OUT("apps_list", "a(tsbby)", "List of apps")
1140 METHOD_ARGS_END
1142 METHOD_ARGS_BEGIN(RunCustom, "Start application by supplying commandline")
1143 METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1144 METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1145 METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "Name")
1146 METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1147 METHOD_ARGS_END
1149 METHOD_ARGS_BEGIN(StartApp, "Start application")
1150 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1151 METHOD_ARGS_END
1153 METHOD_ARGS_BEGIN(StopApp, "Stop application")
1154 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1155 METHOD_ARGS_END
1157 METHOD_ARGS_BEGIN(KillApp, "Kill application")
1158 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1159 METHOD_ARGS_END
1161 METHOD_ARGS_BEGIN(RemoveApp, "Remove application")
1162 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1163 METHOD_ARGS_END
1165 METHOD_ARGS_BEGIN(GetAppProperties, "Get properties of an application")
1166 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1167 METHOD_ARG_DESCRIBE_OUT("name", DBUS_TYPE_STRING_AS_STRING, "")
1168 METHOD_ARG_DESCRIBE_OUT("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1169 METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1170 METHOD_ARG_DESCRIBE_OUT("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1171 METHOD_ARG_DESCRIBE_OUT("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1172 METHOD_ARGS_END
1174 METHOD_ARGS_BEGIN(SetAppProperties, "Set properties of an application")
1175 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1176 METHOD_ARG_DESCRIBE_IN("name", DBUS_TYPE_STRING_AS_STRING, "")
1177 METHOD_ARG_DESCRIBE_IN("commandline", DBUS_TYPE_STRING_AS_STRING, "Commandline")
1178 METHOD_ARG_DESCRIBE_IN("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1179 METHOD_ARG_DESCRIBE_IN("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1180 METHOD_ARGS_END
1182 METHOD_ARGS_BEGIN(IsAppRunning, "Check whether application is running")
1183 METHOD_ARG_DESCRIBE_IN("id", DBUS_TYPE_UINT64_AS_STRING, "id of app")
1184 METHOD_ARG_DESCRIBE_OUT("running", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether app is running")
1185 METHOD_ARGS_END
1188 METHODS_BEGIN
1189 METHOD_DESCRIBE(GetAll, get_all) /* sync */
1190 METHOD_DESCRIBE(RunCustom, run_custom) /* async */
1191 METHOD_DESCRIBE(StartApp, start_app) /* async */
1192 METHOD_DESCRIBE(StopApp, stop_app) /* async */
1193 METHOD_DESCRIBE(KillApp, kill_app) /* async */
1194 METHOD_DESCRIBE(GetAppProperties, get_app_properties) /* sync */
1195 METHOD_DESCRIBE(SetAppProperties, set_app_properties) /* sync */
1196 METHOD_DESCRIBE(RemoveApp, remove_app) /* sync */
1197 METHOD_DESCRIBE(IsAppRunning, is_app_running) /* sync */
1198 METHODS_END
1200 SIGNAL_ARGS_BEGIN(AppAdded, "")
1201 SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1202 SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1203 SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
1204 SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1205 SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1206 SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1207 SIGNAL_ARGS_END
1209 SIGNAL_ARGS_BEGIN(AppRemoved, "")
1210 SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1211 SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1212 SIGNAL_ARGS_END
1214 SIGNAL_ARGS_BEGIN(AppStateChanged, "")
1215 SIGNAL_ARG_DESCRIBE("new_list_version", DBUS_TYPE_UINT64_AS_STRING, "")
1216 SIGNAL_ARG_DESCRIBE("id", DBUS_TYPE_UINT64_AS_STRING, "")
1217 SIGNAL_ARG_DESCRIBE("name", DBUS_TYPE_STRING_AS_STRING, "")
1218 SIGNAL_ARG_DESCRIBE("running", DBUS_TYPE_BOOLEAN_AS_STRING, "")
1219 SIGNAL_ARG_DESCRIBE("terminal", DBUS_TYPE_BOOLEAN_AS_STRING, "Whether to run in terminal")
1220 SIGNAL_ARG_DESCRIBE("level", DBUS_TYPE_BYTE_AS_STRING, "Level")
1221 SIGNAL_ARGS_END
1223 SIGNALS_BEGIN
1224 SIGNAL_DESCRIBE(AppAdded)
1225 SIGNAL_DESCRIBE(AppRemoved)
1226 SIGNAL_DESCRIBE(AppStateChanged)
1227 SIGNALS_END
1229 INTERFACE_BEGIN(g_iface_app_supervisor, IFACE_APP_SUPERVISOR)
1230 INTERFACE_DEFAULT_HANDLER
1231 INTERFACE_EXPOSE_METHODS
1232 INTERFACE_EXPOSE_SIGNALS
1233 INTERFACE_END