@@ -538,6 +538,82 @@ uint32_t CheckPermissions(const std::string& name, const std::string& value,
538538 return PROP_SUCCESS;
539539}
540540
541+ static timer_t auto_reboot_timer;
542+ static bool device_unlocked_at_least_once;
543+ static bool is_auto_reboot_timer_started;
544+
545+ static void auto_reboot_timer_callback (union sigval) {
546+ LOG (INFO) << " auto_reboot: received timer callback, rebooting" ;
547+ trigger_shutdown (" reboot" );
548+ }
549+
550+ // all of the auto_reboot_* functions, except for auto_reboot_timer_callback, are called on the
551+ // same thread
552+
553+ static void auto_reboot_timer_init () {
554+ struct sigevent sev = {};
555+ sev.sigev_notify = SIGEV_THREAD;
556+ sev.sigev_notify_function = auto_reboot_timer_callback;
557+
558+ if (int r = timer_create (CLOCK_BOOTTIME_ALARM, &sev, &auto_reboot_timer); r != 0 ) {
559+ LOG (FATAL) << " auto_reboot: timer_create failed: " << strerror (errno);
560+ }
561+ }
562+
563+ static void auto_reboot_timer_set (time_t duration_sec) {
564+ const time_t min_duration = 20 ;
565+ if (duration_sec != 0 && duration_sec < min_duration) {
566+ LOG (WARNING) << " auto_reboot: raised timer duration from " << duration_sec << " to " << min_duration << " seconds" ;
567+ duration_sec = min_duration;
568+ }
569+ struct itimerspec ts_dur = {};
570+ ts_dur.it_value .tv_sec = duration_sec;
571+ struct itimerspec ts_prev = {};
572+ int flags = 0 ;
573+ if (int r = timer_settime (auto_reboot_timer, flags, &ts_dur, &ts_prev); r != 0 ) {
574+ LOG (FATAL) << " auto_reboot: timer_settime failed: " << strerror (errno);
575+ }
576+ if (duration_sec > 0 ) {
577+ LOG (INFO) << " auto_reboot: started timer for " << duration_sec << " seconds" ;
578+ }
579+ LOG (DEBUG) << " auto_reboot: prev timer value: " << ts_prev.it_value .tv_sec << " seconds" ;
580+ }
581+
582+ static int auto_reboot_handle_property_set (const std::string& value) {
583+ LOG (DEBUG) << " auto_reboot: handle_property_set: " << value;
584+ if (value == " on_device_unlocked" ) {
585+ device_unlocked_at_least_once = true ;
586+ if (is_auto_reboot_timer_started) {
587+ auto_reboot_timer_set (0 );
588+ is_auto_reboot_timer_started = false ;
589+ LOG (INFO) << " auto_reboot: on_device_unlocked: stopped timer" ;
590+ } else {
591+ LOG (INFO) << " auto_reboot: on_device_unlocked: no started timer" ;
592+ }
593+ return PROP_SUCCESS;
594+ }
595+
596+ int duration_sec = atoi (value.c_str ()); // std::stoi can throw
597+ if (duration_sec <= 0 || (uint64_t ) duration_sec > (uint64_t ) std::numeric_limits<time_t >::max ()) {
598+ LOG (WARNING) << " auto_reboot: invalid prop value: " << value;
599+ return PROP_ERROR_INVALID_VALUE;
600+ }
601+
602+ if (device_unlocked_at_least_once) {
603+ if (is_auto_reboot_timer_started) {
604+ LOG (INFO) << " auto_reboot: timer is already started, ignored request to restart it;"
605+ << " requested timer duration: " << value << " seconds" ;
606+ } else {
607+ auto_reboot_timer_set ((time_t ) duration_sec);
608+ is_auto_reboot_timer_started = true ;
609+ }
610+ } else {
611+ LOG (INFO) << " auto_reboot: device was never unlocked, skipped setting timer" ;
612+ }
613+
614+ return PROP_SUCCESS;
615+ }
616+
541617// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*, or std::nullopt
542618// if asynchronous.
543619std::optional<uint32_t > HandlePropertySet (const std::string& name, const std::string& value,
@@ -580,6 +656,11 @@ std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::st
580656 return {PROP_SUCCESS};
581657 }
582658
659+ if (name == " sys.auto_reboot_ctl" ) {
660+ int res = auto_reboot_handle_property_set (value);
661+ return {res};
662+ }
663+
583664 return PropertySet (name, value, socket, error);
584665}
585666
@@ -1466,6 +1547,8 @@ static void PropertyServiceThread(int fd, bool listen_init) {
14661547 }
14671548 }
14681549
1550+ auto_reboot_timer_init ();
1551+
14691552 while (true ) {
14701553 auto epoll_result = epoll.Wait (std::nullopt );
14711554 if (!epoll_result.ok ()) {
0 commit comments