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