usb_moded  0.86.0+mer57
usb_moded-worker.c
Go to the documentation of this file.
1 
25 #include "usb_moded-worker.h"
26 
27 #include "usb_moded.h"
28 #include "usb_moded-android.h"
29 #include "usb_moded-configfs.h"
30 #include "usb_moded-control.h"
31 #include "usb_moded-log.h"
32 #include "usb_moded-modes.h"
33 #include "usb_moded-modesetting.h"
34 #include "usb_moded-modules.h"
35 #include "usb_moded-appsync.h"
36 
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/eventfd.h>
40 
41 #include <pthread.h> // NOTRIM
42 #include <unistd.h>
43 #include <pwd.h>
44 
45 /* ========================================================================= *
46  * Types
47  * ========================================================================= */
48 
50 typedef enum {
57 } devstate_t;
58 
59 static const char * const devstate_name[] = {
60  [DEVSTATE_UNKNOWN] = "unknown",
61  [DEVSTATE_UNMOUNTED] = "unmounted",
62  [DEVSTATE_MOUNTED] = "mounted",
63 };
64 
65 /* ========================================================================= *
66  * Prototypes
67  * ========================================================================= */
68 
69 /* ------------------------------------------------------------------------- *
70  * WORKER
71  * ------------------------------------------------------------------------- */
72 
73 static bool worker_thread_p (void);
74 bool worker_bailing_out (void);
75 static devstate_t worker_get_mtp_device_state (void);
76 static void worker_unmount_mtp_device (void);
77 static bool worker_mount_mtp_device (void);
78 static bool worker_mode_is_mtp_mode (const char *mode);
79 static bool worker_is_mtpd_running (void);
80 static bool worker_mtpd_running_p (void *aptr);
81 static bool worker_mtpd_stopped_p (void *aptr);
82 static bool worker_stop_mtpd (void);
83 static bool worker_start_mtpd (void);
84 static bool worker_switch_to_charging (void);
85 const char *worker_get_kernel_module (void);
86 bool worker_set_kernel_module (const char *module);
87 void worker_clear_kernel_module (void);
90 void worker_set_usb_mode_data (const modedata_t *data);
91 static const char *worker_get_activated_mode_locked(void);
92 static bool worker_set_activated_mode_locked(const char *mode);
93 static const char *worker_get_requested_mode_locked(void);
94 static bool worker_set_requested_mode_locked(const char *mode);
95 void worker_request_hardware_mode (const char *mode);
96 void worker_clear_hardware_mode (void);
97 static void worker_execute (void);
98 static void worker_switch_to_mode (const char *mode);
99 static guint worker_add_iowatch (int fd, bool close_on_unref, GIOCondition cnd, GIOFunc io_cb, gpointer aptr);
100 static void *worker_thread_cb (void *aptr);
101 static gboolean worker_notify_cb (GIOChannel *chn, GIOCondition cnd, gpointer data);
102 static bool worker_start_thread (void);
103 static void worker_stop_thread (void);
104 static void worker_delete_eventfd (void);
105 static bool worker_create_eventfd (void);
106 bool worker_init (void);
107 void worker_quit (void);
108 void worker_wakeup (void);
109 static void worker_notify (void);
110 
111 /* ========================================================================= *
112  * Data
113  * ========================================================================= */
114 
115 static pthread_t worker_thread_id = 0;
116 
117 static pthread_mutex_t worker_mutex = PTHREAD_MUTEX_INITIALIZER;
118 
124 static volatile bool worker_bailout_requested = false;
125 
131 static volatile bool worker_bailout_handled = false;
132 
133 #define WORKER_LOCKED_ENTER do {\
134  if( pthread_mutex_lock(&worker_mutex) != 0 ) { \
135  log_crit("WORKER LOCK FAILED");\
136  _exit(EXIT_FAILURE);\
137  }\
138 }while(0)
139 
140 #define WORKER_LOCKED_LEAVE do {\
141  if( pthread_mutex_unlock(&worker_mutex) != 0 ) { \
142  log_crit("WORKER UNLOCK FAILED");\
143  _exit(EXIT_FAILURE);\
144  }\
145 }while(0)
146 
147 /* ========================================================================= *
148  * Functions
149  * ========================================================================= */
150 
151 static bool
152 worker_thread_p(void)
153 {
154  LOG_REGISTER_CONTEXT;
155 
156  return worker_thread_id && worker_thread_id == pthread_self();
157 }
158 
159 bool
160 worker_bailing_out(void)
161 {
162  LOG_REGISTER_CONTEXT;
163 
164  // ref: see common_msleep_()
165  return (worker_thread_p() &&
166  worker_bailout_requested &&
167  !worker_bailout_handled);
168 }
169 
170 /* ------------------------------------------------------------------------- *
171  * MTP_DEVICE
172  * ------------------------------------------------------------------------- */
173 
191 static devstate_t
192 worker_get_mtp_device_state(void)
193 {
194  LOG_REGISTER_CONTEXT;
195 
197 
198  if( access("/dev/mtp/ep0", F_OK) == 0 )
199  state = DEVSTATE_MOUNTED;
200  else if( errno == ENOENT )
201  state = DEVSTATE_UNMOUNTED;
202  else
203  log_warning("/dev/mtp/ep0: %m");
204 
205  log_debug("mtp device state = %s", devstate_name[state]);
206  return state;
207 }
208 
211 static void
212 worker_unmount_mtp_device(void)
213 {
214  LOG_REGISTER_CONTEXT;
215 
216  if( worker_get_mtp_device_state() != DEVSTATE_UNMOUNTED ) {
217  log_debug("unmounting mtp device");
218  common_system("/bin/umount /dev/mtp");
219  }
220 }
221 
229 static bool
230 worker_mount_mtp_device(void)
231 {
232  LOG_REGISTER_CONTEXT;
233 
234  bool mounted = false;
235 
236  /* Fail if control endpoint is already present */
237  if( worker_get_mtp_device_state() != DEVSTATE_UNMOUNTED ) {
238  log_err("mtp device already mounted");
239  goto EXIT;
240  }
241 
242  /* Ensure that device directory exists */
243  if( mkdir("/dev/mtp", 0755) == -1 && errno != EEXIST ) {
244  log_err("failed to create /dev/mtp directory: %m");
245  goto EXIT;
246  }
247 
248  /* Probe currently active user for uid/gid info. In case these
249  * can't be obtained, use values for default user as fallback. */
250  gid_t gid = 100000;
251  uid_t uid = usbmoded_get_current_user();
252  if( uid == UID_UNKNOWN )
253  uid = 100000;
254 
255  struct passwd *pw = getpwuid(uid);
256  if( pw )
257  gid = pw->pw_gid;
258 
259  /* Attempt to mount mtp device using root uid and primary
260  * gid of the current user.
261  */
262  char cmd[256];
263  snprintf(cmd, sizeof cmd,
264  "/bin/mount -o mode=0770,uid=0,gid=%u -t functionfs mtp /dev/mtp",
265  (unsigned)gid);
266 
267  log_debug("mounting mtp device");
268  if( common_system(cmd) != 0 )
269  goto EXIT;
270 
271  /* Check that control endpoint is present */
272  if( worker_get_mtp_device_state() != DEVSTATE_MOUNTED ) {
273  log_err("mtp control not mounted");
274  goto EXIT;
275  }
276 
277  mounted = true;
278 
279 EXIT:
280  return mounted;
281 }
282 
283 /* ------------------------------------------------------------------------- *
284  * MTP_DAEMON
285  * ------------------------------------------------------------------------- */
286 
297 static unsigned worker_mtp_start_delay = 120 * 1000;
298 
305 static unsigned worker_mtp_stop_delay = 15 * 1000;
306 
313 static bool worker_mtp_service_started = false;
314 
315 static bool worker_mode_is_mtp_mode(const char *mode)
316 {
317  LOG_REGISTER_CONTEXT;
318 
319  return mode && !strcmp(mode, "mtp_mode");
320 }
321 
322 static bool worker_is_mtpd_running(void)
323 {
324  LOG_REGISTER_CONTEXT;
325 
326  /* ep0 becomes available when /dev/mtp is mounted.
327  *
328  * ep1, ep2, ep3 exist while mtp daemon is running,
329  * has ep0 opened and has written config data to it.
330  */
331  static const char * const lut[] = {
332  "/dev/mtp/ep0",
333  "/dev/mtp/ep1",
334  "/dev/mtp/ep2",
335  "/dev/mtp/ep3",
336  0
337  };
338 
339  bool ack = true;
340 
341  for( size_t i = 0; lut[i]; ++i ) {
342  if( access(lut[i], F_OK) == -1 ) {
343  ack = false;
344  break;
345  }
346  }
347 
348  return ack;
349 }
350 
351 static bool
352 worker_mtpd_running_p(void *aptr)
353 {
354  LOG_REGISTER_CONTEXT;
355 
356  (void)aptr;
357  return worker_is_mtpd_running();
358 }
359 
360 static bool
361 worker_mtpd_stopped_p(void *aptr)
362 {
363  LOG_REGISTER_CONTEXT;
364 
365  (void)aptr;
366  return !worker_is_mtpd_running();
367 }
368 
369 static bool
370 worker_stop_mtpd(void)
371 {
372  LOG_REGISTER_CONTEXT;
373 
374  bool ack = false;
375 
376  if( !worker_mtp_service_started && worker_mtpd_stopped_p(0) ) {
377  log_debug("mtp daemon is not running");
378  goto SUCCESS;
379  }
380 
381  int rc = common_system("systemctl-user stop buteo-mtp.service");
382  if( rc != 0 ) {
383  log_warning("failed to stop mtp daemon; exit code = %d", rc);
384  goto FAILURE;
385  }
386 
387  /* Have succesfully stopped mtp service */
388  worker_mtp_service_started = false;
389 
390  if( common_wait(worker_mtp_stop_delay, worker_mtpd_stopped_p, 0) != WAIT_READY ) {
391  log_warning("failed to stop mtp daemon; giving up");
392  goto FAILURE;
393  }
394 
395  log_debug("mtp daemon has stopped");
396 
397 SUCCESS:
398  ack = true;
399 
400 FAILURE:
401  return ack;
402 }
403 
404 static bool
405 worker_start_mtpd(void)
406 {
407  LOG_REGISTER_CONTEXT;
408 
409  bool ack = false;
410 
411  if( worker_mtpd_running_p(0) ) {
412  log_debug("mtp daemon is running");
413  goto SUCCESS;
414  }
415 
416  /* Have attempted to start mtp service */
417  worker_mtp_service_started = true;
418 
419  int rc = common_system("systemctl-user start buteo-mtp.service");
420  if( rc != 0 ) {
421  log_warning("failed to start mtp daemon; exit code = %d", rc);
422  goto FAILURE;
423  }
424 
425  if( common_wait(worker_mtp_start_delay, worker_mtpd_running_p, 0) != WAIT_READY ) {
426  log_warning("failed to start mtp daemon; giving up");
427  goto FAILURE;
428  }
429 
430  log_debug("mtp daemon has started");
431 
432 SUCCESS:
433  ack = true;
434 
435 FAILURE:
436  return ack;
437 }
438 
439 static bool worker_switch_to_charging(void)
440 {
441  LOG_REGISTER_CONTEXT;
442 
443  bool ack = true;
444 
445  if( android_set_charging_mode() )
446  goto SUCCESS;
447 
448  if( configfs_set_charging_mode() )
449  goto SUCCESS;
450 
451  if( modules_in_use() ) {
452  if( worker_set_kernel_module(MODULE_MASS_STORAGE) )
453  goto SUCCESS;
454  worker_set_kernel_module(MODULE_NONE);
455  }
456 
457  log_err("switch to charging mode failed");
458 
459  ack = false;
460 SUCCESS:
461  return ack;
462 }
463 
464 /* ------------------------------------------------------------------------- *
465  * KERNEL_MODULE
466  * ------------------------------------------------------------------------- */
467 
469 static char *worker_kernel_module = NULL;
470 
476 const char * worker_get_kernel_module(void)
477 {
478  LOG_REGISTER_CONTEXT;
479 
480  return worker_kernel_module ?: MODULE_NONE;
481 }
482 
488 bool worker_set_kernel_module(const char *module)
489 {
490  LOG_REGISTER_CONTEXT;
491 
492  bool ack = false;
493 
494  if( !module )
495  module = MODULE_NONE;
496 
497  const char *current = worker_get_kernel_module();
498 
499  log_debug("current module: %s -> %s", current, module);
500 
501  if( !g_strcmp0(current, module) )
502  goto SUCCESS;
503 
504  if( modules_unload_module(current) != 0 )
505  goto EXIT;
506 
507  free(worker_kernel_module), worker_kernel_module = 0;
508 
509  if( modules_load_module(module) != 0 )
510  goto EXIT;
511 
512  if( g_strcmp0(module, MODULE_NONE) )
513  worker_kernel_module = strdup(module);
514 
515 SUCCESS:
516  ack = true;
517 EXIT:
518  return ack;
519 }
520 
521 void worker_clear_kernel_module(void)
522 {
523  LOG_REGISTER_CONTEXT;
524 
525  free(worker_kernel_module), worker_kernel_module = 0;
526 }
527 
528 /* ------------------------------------------------------------------------- *
529  * MODE_DATA
530  * ------------------------------------------------------------------------- */
531 
533 static modedata_t *worker_mode_data = NULL;
534 
542 {
543  LOG_REGISTER_CONTEXT;
544 
545  return worker_mode_data;
546 }
547 
555 {
556  LOG_REGISTER_CONTEXT;
557 
558  WORKER_LOCKED_ENTER;
559 
560  modedata_t *modedata = modedata_copy(worker_mode_data);
561 
562  WORKER_LOCKED_LEAVE;
563 
564  return modedata;;
565 }
566 
574 {
575  LOG_REGISTER_CONTEXT;
576 
577  WORKER_LOCKED_ENTER;
578 
579  modedata_free(worker_mode_data),
580  worker_mode_data = modedata_copy(data);
581 
582  WORKER_LOCKED_LEAVE;
583 }
584 
585 /* ------------------------------------------------------------------------- *
586  * HARDWARE_MODE
587  * ------------------------------------------------------------------------- */
588 
589 /* The hardware mode name
590  *
591  * How the usb hardware has been configured.
592  *
593  * For example internal_mode=MODE_ASK gets
594  * mapped to hardware_mode=MODE_CHARGING */
595 static gchar *worker_requested_mode = NULL;
596 
597 static gchar *worker_activated_mode = NULL;
598 
599 static const char *
600 worker_get_activated_mode_locked(void)
601 {
602  LOG_REGISTER_CONTEXT;
603 
604  return worker_activated_mode ?: MODE_UNDEFINED;
605 }
606 
607 static bool
608 worker_set_activated_mode_locked(const char *mode)
609 {
610  LOG_REGISTER_CONTEXT;
611 
612  bool changed = false;
613  const char *prev = worker_get_activated_mode_locked();
614 
615  if( !g_strcmp0(prev, mode) )
616  goto EXIT;
617 
618  log_debug("activated_mode: %s -> %s", prev, mode);
619  g_free(worker_activated_mode),
620  worker_activated_mode = g_strdup(mode);
621  changed = true;
622 
623 EXIT:
624  return changed;
625 }
626 
627 static const char *
628 worker_get_requested_mode_locked(void)
629 {
630  LOG_REGISTER_CONTEXT;
631 
632  return worker_requested_mode ?: MODE_UNDEFINED;
633 }
634 
635 static bool
636 worker_set_requested_mode_locked(const char *mode)
637 {
638  LOG_REGISTER_CONTEXT;
639 
640  bool changed = false;
641  const char *prev = worker_get_requested_mode_locked();
642 
643  if( !g_strcmp0(prev, mode) )
644  goto EXIT;
645 
646  log_debug("requested_mode: %s -> %s", prev, mode);
647  g_free(worker_requested_mode),
648  worker_requested_mode = g_strdup(mode);
649  changed = true;
650 
651 EXIT:
652  return changed;
653 }
654 
655 void worker_request_hardware_mode(const char *mode)
656 {
657  LOG_REGISTER_CONTEXT;
658 
659  WORKER_LOCKED_ENTER;
660 
661  if( !worker_set_requested_mode_locked(mode) )
662  goto EXIT;
663 
664  worker_wakeup();
665 
666 EXIT:
667  WORKER_LOCKED_LEAVE;
668  return;
669 }
670 
671 void worker_clear_hardware_mode(void)
672 {
673  LOG_REGISTER_CONTEXT;
674 
675  WORKER_LOCKED_ENTER;
676  g_free(worker_requested_mode), worker_requested_mode = 0;
677  WORKER_LOCKED_LEAVE;
678 }
679 
680 static void
681 worker_execute(void)
682 {
683  LOG_REGISTER_CONTEXT;
684 
685  WORKER_LOCKED_ENTER;
686 
687  const char *activated = worker_get_activated_mode_locked();
688  const char *requested = worker_get_requested_mode_locked();
689  const char *activate = common_map_mode_to_hardware(requested);
690 
691  log_debug("activated = %s", activated);
692  log_debug("requested = %s", requested);
693  log_debug("activate = %s", activate);
694 
695  bool changed = g_strcmp0(activated, activate) != 0;
696  gchar *mode = g_strdup(activate);
697 
698  WORKER_LOCKED_LEAVE;
699 
700  if( changed )
701  worker_switch_to_mode(mode);
702  else
703  worker_notify();
704 
705  g_free(mode);
706 
707  return;
708 }
709 
710 /* ------------------------------------------------------------------------- *
711  * MODE_SWITCH
712  * ------------------------------------------------------------------------- */
713 
714 static void
715 worker_switch_to_mode(const char *mode)
716 {
717  LOG_REGISTER_CONTEXT;
718 
719  const char *override = 0;
720  modedata_t *data = 0;
721 
722  /* set return to 1 to be sure to error out if no matching mode is found either */
723 
724  log_debug("Cleaning up previous mode");
725 
726  /* Either mtp daemon is not needed, or it must be *started* in
727  * correct phase of gadget configuration when entering mtp mode.
728  *
729  * Similarly, unmount mtp device to make sure sure it gets mounted
730  * with appropriate uid/gid values when it is actually needed.
731  */
732  worker_stop_mtpd();
733  worker_unmount_mtp_device();
734 
735  if( worker_get_usb_mode_data() ) {
736  modesetting_leave_dynamic_mode();
738  }
739 
740  /* Mode specific applications have been stopped and we can
741  * take updated appsync configuration in use.
742  */
744 
745  log_debug("Setting %s\n", mode);
746 
747  /* Mode mapping should mean we only see MODE_CHARGING here, but just
748  * in case redirect fixed charging related things to charging ... */
749 
750  if( !strcmp(mode, MODE_CHARGING) ||
751  !strcmp(mode, MODE_CHARGING_FALLBACK) ||
752  !strcmp(mode, MODE_CHARGER) ||
753  !strcmp(mode, MODE_UNDEFINED) ||
754  !strcmp(mode, MODE_ASK)) {
755  goto CHARGE;
756  }
757 
758  if( !usbmoded_can_export() ) {
759  log_warning("Policy does not allow mode: %s", mode);
760  goto FAILED;
761  }
762 
763  if( (data = usbmoded_dup_modedata(mode)) ) {
764  log_debug("Matching mode %s found.\n", mode);
765 
766  /* set data before calling any of the dynamic mode functions
767  * as they will use the worker_get_usb_mode_data function */
769 
770  /* When dealing with configfs, we can't enable UDC without
771  * already having mtpd running */
772  if( worker_mode_is_mtp_mode(mode) && configfs_in_use() ) {
773  if( !worker_mount_mtp_device() )
774  goto FAILED;
775  if( !worker_start_mtpd() )
776  goto FAILED;
777  }
778 
780  goto FAILED;
781 
782  if( !modesetting_enter_dynamic_mode() )
783  goto FAILED;
784 
785  /* When dealing with android usb, it must be enabled before
786  * we can start mtpd. Assumption is that the same applies
787  * when using kernel modules. */
788  if( worker_mode_is_mtp_mode(mode) && !configfs_in_use() ) {
789  if( !worker_mount_mtp_device() )
790  goto FAILED;
791  if( !worker_start_mtpd() )
792  goto FAILED;
793  }
794 
795  goto SUCCESS;
796  }
797 
798  log_warning("Matching mode %s was not found.", mode);
799 
800 FAILED:
801  worker_bailout_handled = true;
802 
803  /* Undo any changes we might have might have already done */
804  if( worker_get_usb_mode_data() ) {
805  log_debug("Cleaning up failed mode switch");
806  worker_stop_mtpd();
807  modesetting_leave_dynamic_mode();
809  }
810 
811  /* From usb configuration point of view MODE_UNDEFINED and
812  * MODE_CHARGING are the same, but for the purposes of exposing
813  * a sane state over D-Bus we need to differentiate between
814  * "failure to set mode" and "aborting mode setting due to cable
815  * disconnect" by inspecting whether target mode has been
816  * switched to undefined.
817  */
818  WORKER_LOCKED_ENTER;
819  const char *requested = worker_get_requested_mode_locked();
820  if( !g_strcmp0(requested, MODE_UNDEFINED) )
821  override = MODE_UNDEFINED;
822  else
823  override = MODE_CHARGING;
824  WORKER_LOCKED_LEAVE;
825  log_warning("mode setting failed, try %s", override);
826 
827 CHARGE:
828  if( worker_switch_to_charging() )
829  goto SUCCESS;
830 
831  log_crit("failed to activate charging, all bets are off");
832 
833  /* FIXME: double check this error path */
834 
835  /* If we get here then usb_module loading failed,
836  * no mode matched, and charging setup failed too.
837  */
838 
839  override = MODE_UNDEFINED;
840  log_warning("mode setting failed, fallback to %s", override);
841  worker_set_kernel_module(MODULE_NONE);
842 
843 SUCCESS:
844 
845  WORKER_LOCKED_ENTER;
846  if( override ) {
847  worker_set_requested_mode_locked(override);
848  override = common_map_mode_to_hardware(override);
849  worker_set_activated_mode_locked(override);
850  }
851  else {
852  worker_set_activated_mode_locked(mode);
853  }
854  WORKER_LOCKED_LEAVE;
855 
856  worker_notify();
857 
858  modedata_free(data);
859 
860  return;
861 }
862 
863 /* ------------------------------------------------------------------------- *
864  * WORKER_THREAD
865  * ------------------------------------------------------------------------- */
866 
868 static int worker_req_evfd = -1;
869 
871 static int worker_rsp_evfd = -1;
872 
874 static guint worker_rsp_wid = 0;
875 
876 static guint
877 worker_add_iowatch(int fd, bool close_on_unref,
878  GIOCondition cnd, GIOFunc io_cb, gpointer aptr)
879 {
880  LOG_REGISTER_CONTEXT;
881 
882  guint wid = 0;
883  GIOChannel *chn = 0;
884 
885  if( !(chn = g_io_channel_unix_new(fd)) )
886  goto cleanup;
887 
888  g_io_channel_set_close_on_unref(chn, close_on_unref);
889 
890  cnd |= G_IO_ERR | G_IO_HUP | G_IO_NVAL;
891 
892  if( !(wid = g_io_add_watch(chn, cnd, io_cb, aptr)) )
893  goto cleanup;
894 
895 cleanup:
896  if( chn != 0 ) g_io_channel_unref(chn);
897 
898  return wid;
899 
900 }
901 
902 static void *worker_thread_cb(void *aptr)
903 {
904  LOG_REGISTER_CONTEXT;
905 
906  (void)aptr;
907 
908  /* Async cancellation, but disabled */
909  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
910  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
911 
912  /* Leave INT/TERM signal processing up to the main thread */
913  sigset_t ss;
914  sigemptyset(&ss);
915  sigaddset(&ss, SIGINT);
916  sigaddset(&ss, SIGTERM);
917  pthread_sigmask(SIG_BLOCK, &ss, 0);
918 
919  /* Loop until explicitly canceled */
920  for( ;; ) {
921  /* Async cancellation point at wait() */
922  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
923  uint64_t cnt = 0;
924  int rc = read(worker_req_evfd, &cnt, sizeof cnt);
925  pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
926 
927  if( rc == -1 ) {
928  if( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
929  continue;
930  log_err("read: %m");
931  goto EXIT;
932  }
933 
934  if( rc != sizeof cnt )
935  continue;
936 
937  if( cnt > 0 ) {
938  worker_bailout_requested = false;
939  worker_bailout_handled = false;
940  worker_execute();
941  }
942 
943  }
944 EXIT:
945  return 0;
946 }
947 
948 static gboolean
949 worker_notify_cb(GIOChannel *chn, GIOCondition cnd, gpointer data)
950 {
951  LOG_REGISTER_CONTEXT;
952 
953  (void)data;
954 
955  gboolean keep_going = FALSE;
956 
957  if( !worker_rsp_wid )
958  goto cleanup_nak;
959 
960  int fd = g_io_channel_unix_get_fd(chn);
961 
962  if( fd < 0 )
963  goto cleanup_nak;
964 
965  if( cnd & ~G_IO_IN )
966  goto cleanup_nak;
967 
968  if( !(cnd & G_IO_IN) )
969  goto cleanup_ack;
970 
971  uint64_t cnt = 0;
972 
973  int rc = read(fd, &cnt, sizeof cnt);
974 
975  if( rc == 0 ) {
976  log_err("unexpected eof");
977  goto cleanup_nak;
978  }
979 
980  if( rc == -1 ) {
981  if( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
982  goto cleanup_ack;
983 
984  log_err("read error: %m");
985  goto cleanup_nak;
986  }
987 
988  if( rc != sizeof cnt )
989  goto cleanup_nak;
990 
991  {
992  WORKER_LOCKED_ENTER;
993  const char *mode = worker_get_requested_mode_locked();
994  gchar *work = g_strdup(mode);
995  WORKER_LOCKED_LEAVE;
996 
997  control_mode_switched(work);
998  g_free(work);
999  }
1000 
1001 cleanup_ack:
1002  keep_going = TRUE;
1003 
1004 cleanup_nak:
1005 
1006  if( !keep_going ) {
1007  worker_rsp_wid = 0;
1008  log_crit("worker notifications disabled");
1009  }
1010 
1011  return keep_going;
1012 }
1013 
1014 static bool
1015 worker_start_thread(void)
1016 {
1017  LOG_REGISTER_CONTEXT;
1018 
1019  bool ack = false;
1020  int err = pthread_create(&worker_thread_id, 0, worker_thread_cb, 0);
1021  if( err ) {
1022  worker_thread_id = 0;
1023  log_err("failed to start worker thread");
1024  }
1025  else {
1026  ack = true;
1027  log_debug("worker thread started");
1028  }
1029 
1030  return ack;
1031 }
1032 
1033 static void
1034 worker_stop_thread(void)
1035 {
1036  LOG_REGISTER_CONTEXT;
1037 
1038  if( !worker_thread_id )
1039  goto EXIT;
1040 
1041  log_debug("stopping worker thread");
1042  int err = pthread_cancel(worker_thread_id);
1043  if( err ) {
1044  log_err("failed to cancel worker thread");
1045  }
1046  else {
1047  log_debug("waiting for worker thread to exit ...");
1048  void *ret = 0;
1049  struct timespec tmo = { 0, 0};
1050  clock_gettime(CLOCK_REALTIME, &tmo);
1051  tmo.tv_sec += 3;
1052  err = pthread_timedjoin_np(worker_thread_id, &ret, &tmo);
1053  if( err ) {
1054  log_err("worker thread did not exit");
1055  }
1056  else {
1057  log_debug("worker thread terminated");
1058  worker_thread_id = 0;
1059  }
1060  }
1061 
1062  if( worker_thread_id ) {
1063  /* Orderly exit is not safe, just die */
1064  _exit(EXIT_FAILURE);
1065  }
1066 
1067 EXIT:
1068  return;
1069 }
1070 
1071 static void
1072 worker_delete_eventfd(void)
1073 {
1074  LOG_REGISTER_CONTEXT;
1075 
1076  if( worker_req_evfd != -1 )
1077  close(worker_req_evfd), worker_req_evfd = -1;
1078 
1079  if( worker_rsp_wid )
1080  g_source_remove(worker_rsp_wid), worker_rsp_wid = 0;
1081 
1082  if( worker_rsp_evfd != -1 )
1083  close(worker_rsp_evfd), worker_req_evfd = -1;
1084 }
1085 
1086 static bool
1087 worker_create_eventfd(void)
1088 {
1089  LOG_REGISTER_CONTEXT;
1090 
1091  bool ack = false;
1092 
1093  /* Setup notify pipeline */
1094 
1095  if( (worker_rsp_evfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) == -1 )
1096  goto EXIT;
1097 
1098  worker_rsp_wid = worker_add_iowatch(worker_rsp_evfd, false, G_IO_IN,
1099  worker_notify_cb, 0);
1100  if( !worker_rsp_wid )
1101  goto EXIT;
1102 
1103  /* Setup request pipeline */
1104 
1105  if( (worker_req_evfd = eventfd(0, EFD_CLOEXEC)) == -1 )
1106  goto EXIT;
1107 
1108  ack = true;
1109 
1110 EXIT:
1111 
1112  return ack;
1113 }
1114 
1115 bool
1116 worker_init(void)
1117 {
1118  LOG_REGISTER_CONTEXT;
1119 
1120  bool ack = false;
1121 
1122  if( !worker_create_eventfd() )
1123  goto EXIT;
1124 
1125  if( !worker_start_thread() )
1126  goto EXIT;
1127 
1128  ack = true;
1129 
1130 EXIT:
1131  if( !ack )
1132  worker_quit();
1133 
1134  return ack;
1135 }
1136 
1137 void
1138 worker_quit(void)
1139 {
1140  LOG_REGISTER_CONTEXT;
1141 
1142  worker_stop_thread();
1143  worker_delete_eventfd();
1144 
1145  /* Worker thread is stopped and resources can be released. */
1147 }
1148 
1149 void
1150 worker_wakeup(void)
1151 {
1152  LOG_REGISTER_CONTEXT;
1153 
1154  worker_bailout_requested = true;
1155 
1156  uint64_t cnt = 1;
1157  if( write(worker_req_evfd, &cnt, sizeof cnt) == -1 ) {
1158  log_err("failed to signal requested: %m");
1159  }
1160 }
1161 
1162 static void
1163 worker_notify(void)
1164 {
1165  LOG_REGISTER_CONTEXT;
1166 
1167  uint64_t cnt = 1;
1168  if( write(worker_rsp_evfd, &cnt, sizeof cnt) == -1 ) {
1169  log_err("failed to signal handled: %m");
1170  }
1171 }
modedata_free
void modedata_free(modedata_t *self)
Definition: usb_moded-dyn-config.c:76
modules_load_module
int modules_load_module(const char *module)
Definition: usb_moded-modules.c:181
usb_moded-modes.h
worker_dup_usb_mode_data
modedata_t * worker_dup_usb_mode_data(void)
Definition: usb_moded-worker.c:554
usb_moded-modules.h
usb_moded-modesetting.h
devstate_t
devstate_t
Definition: usb_moded-worker.c:50
MODE_ASK
#define MODE_ASK
Definition: usb_moded-modes.h:74
usb_moded-configfs.h
MODE_CHARGING_FALLBACK
#define MODE_CHARGING_FALLBACK
Definition: usb_moded-modes.h:67
usb_moded-android.h
usbmoded_dup_modedata
modedata_t * usbmoded_dup_modedata(const char *modename)
Definition: usb_moded.c:279
appsync_switch_configuration
void appsync_switch_configuration(void)
Definition: usb_moded-appsync.c:332
worker_get_kernel_module
const char * worker_get_kernel_module(void)
Definition: usb_moded-worker.c:476
DEVSTATE_UNKNOWN
@ DEVSTATE_UNKNOWN
Definition: usb_moded-worker.c:52
modedata_t::mode_module
gchar * mode_module
Definition: usb_moded-dyn-config.h:103
usbmoded_can_export
bool usbmoded_can_export(void)
Definition: usb_moded.c:591
usb_moded.h
MODE_CHARGER
#define MODE_CHARGER
Definition: usb_moded-modes.h:60
DEVSTATE_MOUNTED
@ DEVSTATE_MOUNTED
Definition: usb_moded-worker.c:56
modules_unload_module
int modules_unload_module(const char *module)
Definition: usb_moded-modules.c:253
usb_moded-appsync.h
usb_moded-control.h
modedata_copy
modedata_t * modedata_copy(const modedata_t *that)
Definition: usb_moded-dyn-config.c:111
worker_set_usb_mode_data
void worker_set_usb_mode_data(const modedata_t *data)
Definition: usb_moded-worker.c:573
MODE_UNDEFINED
#define MODE_UNDEFINED
Definition: usb_moded-modes.h:51
usbmoded_get_current_user
uid_t usbmoded_get_current_user(void)
Definition: usb_moded.c:572
worker_set_kernel_module
bool worker_set_kernel_module(const char *module)
Definition: usb_moded-worker.c:488
usb_moded-log.h
modedata_t
Definition: usb_moded-dyn-config.h:100
DEVSTATE_UNMOUNTED
@ DEVSTATE_UNMOUNTED
Definition: usb_moded-worker.c:54
MODE_CHARGING
#define MODE_CHARGING
Definition: usb_moded-modes.h:77
usb_moded-worker.h
worker_get_usb_mode_data
const modedata_t * worker_get_usb_mode_data(void)
Definition: usb_moded-worker.c:541