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