usb_moded 0.86.0+mer64
usb_moded-appsync.c
Go to the documentation of this file.
1
29
30#include "usb_moded-appsync.h"
31
32#include "usb_moded.h"
33#include "usb_moded-log.h"
34#include "usb_moded-systemd.h"
35
36#include <unistd.h>
37#include <glob.h>
38
39/* ========================================================================= *
40 * Types
41 * ========================================================================= */
42
53
57typedef struct application_t
58{
59 char *name;
60 char *mode;
61 char *launch;
63 int systemd;
64 int post;
66
67/* ========================================================================= *
68 * Prototypes
69 * ========================================================================= */
70
71/* ------------------------------------------------------------------------- *
72 * APPLICATION
73 * ------------------------------------------------------------------------- */
74
75static bool application_is_valid (const application_t *self);
76static application_t *application_load (const char *filename);
77static void application_free (application_t *self);
78static void application_free_cb (gpointer self);
79static gint application_compare_cb(gconstpointer a, gconstpointer b);
80
81/* ------------------------------------------------------------------------- *
82 * APPLIST
83 * ------------------------------------------------------------------------- */
84
85static void applist_free(GList *list);
86static GList *applist_load(const char *conf_dir);
87
88/* ------------------------------------------------------------------------- *
89 * APPSYNC
90 * ------------------------------------------------------------------------- */
91
95int appsync_activate_pre (const char *mode);
96int appsync_activate_post (const char *mode);
97static int appsync_mark_active_locked (const char *name, int post);
98int appsync_mark_active (const char *name, int post);
99#ifdef APP_SYNC_DBUS
100static gboolean appsync_enumerate_usb_cb (gpointer data);
101static void appsync_start_enumerate_usb_timer (void);
102static void appsync_cancel_enumerate_usb_timer(void);
103static void appsync_enumerate_usb (void);
104#endif
105static void appsync_stop_apps (int post);
106void appsync_deactivate_pre (void);
107void appsync_deactivate_post (void);
108void appsync_deactivate_all (bool force);
109
110/* ========================================================================= *
111 * Data
112 * ========================================================================= */
113
116static pthread_mutex_t appsync_mutex = PTHREAD_MUTEX_INITIALIZER;
117
118#define APPSYNC_LOCKED_ENTER do {\
119 if( pthread_mutex_lock(&appsync_mutex) != 0 ) { \
120 log_crit("APPSYNC LOCK FAILED");\
121 _exit(EXIT_FAILURE);\
122 }\
123}while(0)
124
125#define APPSYNC_LOCKED_LEAVE do {\
126 if( pthread_mutex_unlock(&appsync_mutex) != 0 ) { \
127 log_crit("APPSYNC UNLOCK FAILED");\
128 _exit(EXIT_FAILURE);\
129 }\
130}while(0)
131
133static GList *appsync_apps_curr = NULL;
134
136static GList *appsync_apps_next = NULL;
137static bool appsync_apps_updated = false;
138
139#ifdef APP_SYNC_DBUS
140static guint appsync_enumerate_usb_id = 0;
141static struct timeval appsync_sync_tv = {0, 0};
142static int appsync_no_dbus = 0; // enabled until disabled due to failures
143#else
144static int appsync_no_dbus = 1; // always disabled
145#endif /* APP_SYNC_DBUS */
146
147/* ========================================================================= *
148 * APPLICATION
149 * ========================================================================= */
150
160static bool application_is_valid(const application_t *self)
161{
162 return self && self->name && self->mode && (self->systemd || self->launch);
163}
164
171static application_t *application_load(const char *filename)
172{
173 LOG_REGISTER_CONTEXT;
174
175 application_t *self = NULL;
176 GKeyFile *keyfile = NULL;
177
178 log_debug("loading appsync file: %s", filename);
179
180 if( !(keyfile = g_key_file_new()) )
181 goto cleanup;
182
183 if( !g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, NULL) ) {
184 log_warning("failed to load appsync file: %s", filename);
185 goto cleanup;
186 }
187
188 if( !(self = calloc(1, sizeof *self)) )
189 goto cleanup;
190
191 self->name = g_key_file_get_string(keyfile, APP_INFO_ENTRY, APP_INFO_NAME_KEY, NULL);
192 log_debug("Appname = %s\n", self->name ?: "<unset>");
193
194 self->launch = g_key_file_get_string(keyfile, APP_INFO_ENTRY, APP_INFO_LAUNCH_KEY, NULL);
195 log_debug("Launch = %s\n", self->launch ?: "<unset>");
196
197 self->mode = g_key_file_get_string(keyfile, APP_INFO_ENTRY, APP_INFO_MODE_KEY, NULL);
198 log_debug("Launch mode = %s\n", self->mode ?: "<unset>");
199
200 self->systemd = g_key_file_get_integer(keyfile, APP_INFO_ENTRY, APP_INFO_SYSTEMD_KEY, NULL);
201 log_debug("Systemd control = %d\n", self->systemd);
202
203 self->post = g_key_file_get_integer(keyfile, APP_INFO_ENTRY, APP_INFO_POST, NULL);
204 log_debug("post = %d\n", self->post);
205
207
208cleanup:
209
210 if(keyfile)
211 g_key_file_free(keyfile);
212
213 /* if a minimum set of required elements is not filled in we discard the list_item */
214 if( self && !application_is_valid(self) ) {
215 log_warning("discarding invalid appsync file: %s", filename);
216 application_free(self),
217 self = 0;
218 }
219
220 return self;
221}
222
227static void application_free(application_t *self)
228{
229 LOG_REGISTER_CONTEXT;
230
231 if( self ) {
232 g_free(self->name);
233 g_free(self->launch);
234 g_free(self->mode);
235 free(self);
236 }
237}
238
243static void application_free_cb(gpointer self)
244{
245 LOG_REGISTER_CONTEXT;
246
247 application_free(self);
248}
249
257static gint application_compare_cb(gconstpointer a, gconstpointer b)
258{
259 LOG_REGISTER_CONTEXT;
260
261 const application_t *application_a = a;
262 const application_t *application_b = b;
263 return strcasecmp(application_a->name, application_b->name);
264}
265
266/* ========================================================================= *
267 * APPLIST
268 * ========================================================================= */
269
274static void applist_free(GList *list)
275{
276 g_list_free_full(list, application_free_cb);
277}
278
286static GList *applist_load(const char *conf_dir)
287{
288 LOG_REGISTER_CONTEXT;
289
290 GList *list = 0;
291 gchar *pat = 0;
292 glob_t gb = {};
293
294 if( !(pat = g_strdup_printf("%s/*.ini", conf_dir)) )
295 goto cleanup;
296
297 if( glob(pat, 0, 0, &gb) != 0 ) {
298 log_debug("no appsync ini-files found");
299 goto cleanup;
300 }
301
302 for( size_t i = 0; i < gb.gl_pathc; ++i ) {
303 application_t *application = application_load(gb.gl_pathv[i]);
304 if( application )
305 list = g_list_append(list, application);
306 }
307
308 if( list ) {
309 /* sort list alphabetically so services for a mode
310 * can be run in a certain order */
311 list = g_list_sort(list, application_compare_cb);
312 }
313
314cleanup:
315 globfree(&gb);
316 g_free(pat);
317
318 return list;
319}
320
321/* ========================================================================= *
322 * APPSYNC
323 * ========================================================================= */
324
329{
330 LOG_REGISTER_CONTEXT;
331
332 APPSYNC_LOCKED_ENTER;
333
334 if( appsync_apps_updated ) {
335 appsync_apps_updated = false;
336 log_debug("Switch appsync config");
337 applist_free(appsync_apps_curr),
338 appsync_apps_curr = appsync_apps_next,
339 appsync_apps_next = 0;
340 }
341
342 APPSYNC_LOCKED_LEAVE;
343}
344
348{
349 LOG_REGISTER_CONTEXT;
350
351 APPSYNC_LOCKED_ENTER;
352
353 if( appsync_apps_curr ) {
354 log_debug("Release current appsync config");
355 applist_free(appsync_apps_curr),
356 appsync_apps_curr = 0;
357 }
358
359 if( appsync_apps_next ) {
360 log_debug("Release future appsync config");
361 applist_free(appsync_apps_next),
362 appsync_apps_next = 0;
363 }
364
365 APPSYNC_LOCKED_LEAVE;
366}
367
381{
382 LOG_REGISTER_CONTEXT;
383
384 GList *applist = applist_load(usbmoded_get_diag_mode() ?
385 CONF_DIR_DIAG_PATH : CONF_DIR_PATH);
386
387 APPSYNC_LOCKED_ENTER;
388
389 if( !appsync_apps_curr ) {
390 log_debug("Update current appsync config");
391 appsync_apps_curr = applist;
392
393 applist_free(appsync_apps_next),
394 appsync_apps_next = 0;
395 appsync_apps_updated = false;
396 }
397 else {
398 log_debug("Update future appsync config");
399 applist_free(appsync_apps_next),
400 appsync_apps_next = applist;
401 appsync_apps_updated = true;
402 }
403
404 if( appsync_apps_curr ) {
405 log_debug("Sync list available");
406 /* set up session bus connection if app sync in use
407 * so we do not need to make the time consuming connect
408 * operation at enumeration time ... */
409#ifdef APP_SYNC_DBUS
411#endif
412 }
413
414 APPSYNC_LOCKED_LEAVE;
415}
416
426int appsync_activate_pre(const char *mode)
427{
428 LOG_REGISTER_CONTEXT;
429 int ret = 0; // assume success
430 int count = 0;
431
432 log_debug("activate-pre mode=%s", mode);
433
434 APPSYNC_LOCKED_ENTER;
435
436#ifdef APP_SYNC_DBUS
437 /* Get start of activation timestamp */
438 gettimeofday(&appsync_sync_tv, 0);
439#endif
440
441 if( appsync_apps_curr == 0 )
442 {
443 log_debug("No sync list!");
444#ifdef APP_SYNC_DBUS
445 appsync_enumerate_usb();
446#endif
447 goto cleanup;
448 }
449
450 /* Count apps that need to be activated for this mode and
451 * mark them as currently inactive */
452 for( GList *iter = appsync_apps_curr; iter; iter = g_list_next(iter) )
453 {
454 application_t *application = iter->data;
455
456 if(!strcmp(application->mode, mode))
457 {
458 ++count;
459 application->state = APP_STATE_INACTIVE;
460 }
461 else
462 {
463 application->state = APP_STATE_DONTCARE;
464 }
465 }
466
467 /* If there is nothing to activate, enumerate immediately */
468 if(count <= 0)
469 {
470 log_debug("Nothing to launch\n");
471#ifdef APP_SYNC_DBUS
472 appsync_enumerate_usb();
473#endif
474 goto cleanup;
475 }
476
477#ifdef APP_SYNC_DBUS
478 /* check dbus initialisation, skip dbus activated services if this fails */
479 if(!appsync_no_dbus && !dbusappsync_init())
480 {
481 log_debug("dbus setup failed => skipping dbus launched apps");
482 appsync_no_dbus = 1;
483 }
484
485 /* start timer */
486 appsync_start_enumerate_usb_timer();
487#endif
488
489 /* go through list and launch apps */
490 for( GList *iter = appsync_apps_curr; iter; iter = g_list_next(iter) )
491 {
492 application_t *application = iter->data;
493 if(!strcmp(mode, application->mode))
494 {
495 /* do not launch items marked as post, will be launched after usb is up */
496 if(application->post)
497 {
498 continue;
499 }
500 log_debug("launching pre-enum-app %s", application->name);
501 if(application->systemd)
502 {
503 if(!systemd_control_service(application->name, SYSTEMD_START)) {
504 log_debug("systemd pre-enum-app %s failed", application->name);
505 ret = 1;
506 goto cleanup;
507 }
508 appsync_mark_active_locked(application->name, 0);
509 }
510 else if(application->launch)
511 {
512 /* skipping if dbus session bus is not available,
513 * or not compiled in */
514 if( appsync_no_dbus ) {
515 log_debug("dbus pre-enum-app %s ignored", application->name);
516 /* FIXME: feigning success here allows pre-enum actions
517 * to be "completed" despite of failures or lack
518 * of support for installed configuration items.
519 * Does that make any sense?
520 */
521 appsync_mark_active_locked(application->name, 0);
522 continue;
523 }
524#ifdef APP_SYNC_DBUS
525 if( dbusappsync_launch_app(application->launch) != 0 ) {
526 log_debug("dbus pre-enum-app %s failed", application->name);
527 ret = 1;
528 goto cleanup;
529 }
530 appsync_mark_active_locked(application->name, 0);
531#endif /* APP_SYNC_DBUS */
532 }
533 }
534 }
535
536cleanup:
537 APPSYNC_LOCKED_LEAVE;
538
539 return ret;
540}
541
551int appsync_activate_post(const char *mode)
552{
553 LOG_REGISTER_CONTEXT;
554
555 int ret = 0; // assume success
556
557 log_debug("activate-post mode=%s", mode);
558
559 APPSYNC_LOCKED_ENTER;
560
561 if( !appsync_apps_curr ) {
562 log_debug("No sync list! skipping post sync");
563 goto cleanup;
564 }
565
566#ifdef APP_SYNC_DBUS
567 /* check dbus initialisation, skip dbus activated services if this fails */
568 if(!appsync_no_dbus && !dbusappsync_init())
569 {
570 log_debug("dbus setup failed => skipping dbus launched apps");
571 appsync_no_dbus = 1;
572 }
573#endif /* APP_SYNC_DBUS */
574
575 /* go through list and launch apps */
576 for( GList *iter = appsync_apps_curr; iter; iter = g_list_next(iter) )
577 {
578 application_t *application = iter->data;
579
580 if( !strcmp(application->mode, mode) ) {
581 /* launch only items marked as post, others are already running */
582 if(!application->post)
583 continue;
584
585 log_debug("launching post-enum-app %s\n", application->name);
586 if( application->systemd ) {
587 if(!systemd_control_service(application->name, SYSTEMD_START)) {
588 log_err("systemd post-enum-app %s failed", application->name);
589 ret = 1;
590 break;
591 }
592 appsync_mark_active_locked(application->name, 1);
593 }
594 else if( application->launch ) {
595 /* skipping if dbus session bus is not available,
596 * or not compiled in */
597 if( appsync_no_dbus ) {
598 log_debug("dbus pre-enum-app %s ignored", application->name);
599 continue;
600 }
601#ifdef APP_SYNC_DBUS
602 if( dbusappsync_launch_app(application->launch) != 0 ) {
603 log_err("dbus post-enum-app %s failed", application->name);
604 ret = 1;
605 break;
606 }
607 appsync_mark_active_locked(application->name, 1);
608#endif /* APP_SYNC_DBUS */
609 }
610 }
611 }
612
613cleanup:
614 APPSYNC_LOCKED_LEAVE;
615
616 return ret;
617}
618
630static int appsync_mark_active_locked(const char *name, int post)
631{
632 LOG_REGISTER_CONTEXT;
633
634 int ret = -1; // assume name not found
635 int missing = 0;
636
637 log_debug("%s-enum-app %s is started\n", post ? "post" : "pre", name);
638
639 for( GList *iter = appsync_apps_curr; iter; iter = g_list_next(iter) )
640 {
641 application_t *application = iter->data;
642
643 if(!strcmp(application->name, name))
644 {
645 /* TODO: do we need to worry about duplicate names in the list? */
646 ret = (application->state != APP_STATE_ACTIVE);
647 application->state = APP_STATE_ACTIVE;
648
649 /* updated + missing -> not going to enumerate */
650 if( missing ) break;
651 }
652 else if( application->state == APP_STATE_INACTIVE && application->post == post )
653 {
654 missing = 1;
655
656 /* updated + missing -> not going to enumerate */
657 if( ret != -1 ) break;
658 }
659 }
660 if( !post && !missing )
661 {
662 log_debug("All pre-enum-apps active");
663#ifdef APP_SYNC_DBUS
664 appsync_enumerate_usb();
665#endif
666 }
667
668 /* -1=not found, 0=already active, 1=activated now */
669 return ret;
670}
671
690int appsync_mark_active(const char *name, int post)
691{
692 LOG_REGISTER_CONTEXT;
693
694 APPSYNC_LOCKED_ENTER;
695 int ret = appsync_mark_active_locked(name, post);
696 APPSYNC_LOCKED_LEAVE;
697
698 return ret;
699}
700
701#ifdef APP_SYNC_DBUS
702static gboolean appsync_enumerate_usb_cb(gpointer data)
703{
704 LOG_REGISTER_CONTEXT;
705
706 (void)data;
707 appsync_enumerate_usb_id = 0;
708 log_debug("handling enumeration timeout");
709 appsync_enumerate_usb();
710 /* return false to stop the timer from repeating */
711 return FALSE;
712}
713
714static void appsync_start_enumerate_usb_timer(void)
715{
716 LOG_REGISTER_CONTEXT;
717
718 log_debug("scheduling enumeration timeout");
719 if( appsync_enumerate_usb_id )
720 g_source_remove(appsync_enumerate_usb_id), appsync_enumerate_usb_id = 0;
721 /* NOTE: This was effectively hazard free before blocking mode switch
722 * was offloaded to a worker thread - if APP_SYNC_DBUS is ever
723 * enabled again, this needs to be revisited to avoid timer
724 * scheduled from worker thread getting triggered in mainloop
725 * context before the mode switch activity is finished.
726 */
727 appsync_enumerate_usb_id = g_timeout_add_seconds(2, appsync_enumerate_usb_cb, NULL);
728}
729
730static void appsync_cancel_enumerate_usb_timer(void)
731{
732 LOG_REGISTER_CONTEXT;
733
734 if( appsync_enumerate_usb_id )
735 {
736 log_debug("canceling enumeration timeout");
737 g_source_remove(appsync_enumerate_usb_id), appsync_enumerate_usb_id = 0;
738 }
739}
740
741static void appsync_enumerate_usb(void)
742{
743 LOG_REGISTER_CONTEXT;
744
745 struct timeval tv;
746
747 log_debug("Enumerating");
748
749 /* Stop the timer in case of explicit enumeration call */
750 appsync_cancel_enumerate_usb_timer();
751
752 /* Debug: how long it took from sync start to get here */
753 gettimeofday(&tv, 0);
754 timersub(&tv, &appsync_sync_tv, &tv);
755 log_debug("sync to enum: %.3f seconds", tv.tv_sec + tv.tv_usec * 1e-6);
756
757 /* remove dbus service */
759}
760#endif /* APP_SYNC_DBUS */
761
762/* Internal helper for stopping pre/post apps
763 *
764 * @param post 0=stop pre-apps, or 1=stop post-apps
765 *
766 * @note Assumes that appsync configuration data is already locked.
767 */
768static void appsync_stop_apps(int post)
769{
770 LOG_REGISTER_CONTEXT;
771
772 for( GList *iter = appsync_apps_curr; iter; iter = g_list_next(iter) )
773 {
774 application_t *application = iter->data;
775
776 if( application->post == post &&
777 application->state == APP_STATE_ACTIVE ) {
778
779 log_debug("stopping %s-enum-app %s", post ? "post" : "pre",
780 application->name);
781
782 if( application->systemd ) {
783 if( !systemd_control_service(application->name, SYSTEMD_STOP) )
784 log_debug("Failed to stop %s\n", application->name);
785 }
786 else if( application->launch ) {
787 // NOP
788 }
789 application->state = APP_STATE_DONTCARE;
790 }
791 }
792
793}
794
798{
799 APPSYNC_LOCKED_ENTER;
800 appsync_stop_apps(0);
801 APPSYNC_LOCKED_LEAVE;
802}
803
807{
808 APPSYNC_LOCKED_ENTER;
809 appsync_stop_apps(1);
810 APPSYNC_LOCKED_LEAVE;
811}
812
825{
826 LOG_REGISTER_CONTEXT;
827
828 APPSYNC_LOCKED_ENTER;
829
830 /* If force arg is used, stop all applications that
831 * could have been started by usb-moded */
832 if(force)
833 {
834 log_debug("assuming all applications are active");
835
836 for( GList *iter = appsync_apps_curr; iter; iter = g_list_next(iter) )
837 {
838 application_t *application = iter->data;
839 application->state = APP_STATE_ACTIVE;
840 }
841 }
842
843 /* Stop post-apps 1st */
844 appsync_stop_apps(1);
845
846 /* Then pre-apps */
847 appsync_stop_apps(0);
848
849 /* Do not leave active timers behind */
850#ifdef APP_SYNC_DBUS
851 appsync_cancel_enumerate_usb_timer();
852#endif
853
854 APPSYNC_LOCKED_LEAVE;
855}
app_state_t state
gboolean dbusappsync_init_connection(void)
void dbusappsync_cleanup(void)
gboolean dbusappsync_init(void)
int dbusappsync_launch_app(char *launch)
int appsync_mark_active(const char *name, int post)
void appsync_deactivate_pre(void)
app_state_t
@ APP_STATE_ACTIVE
@ APP_STATE_DONTCARE
@ APP_STATE_INACTIVE
void appsync_switch_configuration(void)
void appsync_free_configuration(void)
void appsync_deactivate_all(bool force)
int appsync_activate_post(const char *mode)
void appsync_deactivate_post(void)
void appsync_load_configuration(void)
int appsync_activate_pre(const char *mode)