@@ -524,6 +524,82 @@ uint32_t CheckPermissions(const std::string& name, const std::string& value,
524524 return PROP_SUCCESS;
525525}
526526
527+ static timer_t auto_reboot_timer;
528+ static bool device_unlocked_at_least_once;
529+ static bool is_auto_reboot_timer_started;
530+
531+ static void auto_reboot_timer_callback (union sigval) {
532+ LOG (INFO) << " auto_reboot: received timer callback, rebooting" ;
533+ trigger_shutdown (" reboot" );
534+ }
535+
536+ // all of the auto_reboot_* functions, except for auto_reboot_timer_callback, are called on the
537+ // same thread
538+
539+ static void auto_reboot_timer_init () {
540+ struct sigevent sev = {};
541+ sev.sigev_notify = SIGEV_THREAD;
542+ sev.sigev_notify_function = auto_reboot_timer_callback;
543+
544+ if (int r = timer_create (CLOCK_BOOTTIME_ALARM, &sev, &auto_reboot_timer); r != 0 ) {
545+ LOG (FATAL) << " auto_reboot: timer_create failed: " << strerror (errno);
546+ }
547+ }
548+
549+ static void auto_reboot_timer_set (time_t duration_sec) {
550+ const time_t min_duration = 20 ;
551+ if (duration_sec != 0 && duration_sec < min_duration) {
552+ LOG (WARNING) << " auto_reboot: raised timer duration from " << duration_sec << " to " << min_duration << " seconds" ;
553+ duration_sec = min_duration;
554+ }
555+ struct itimerspec ts_dur = {};
556+ ts_dur.it_value .tv_sec = duration_sec;
557+ struct itimerspec ts_prev = {};
558+ int flags = 0 ;
559+ if (int r = timer_settime (auto_reboot_timer, flags, &ts_dur, &ts_prev); r != 0 ) {
560+ LOG (FATAL) << " auto_reboot: timer_settime failed: " << strerror (errno);
561+ }
562+ if (duration_sec > 0 ) {
563+ LOG (INFO) << " auto_reboot: started timer for " << duration_sec << " seconds" ;
564+ }
565+ LOG (DEBUG) << " auto_reboot: prev timer value: " << ts_prev.it_value .tv_sec << " seconds" ;
566+ }
567+
568+ static int auto_reboot_handle_property_set (const std::string& value) {
569+ LOG (DEBUG) << " auto_reboot: handle_property_set: " << value;
570+ if (value == " on_device_unlocked" ) {
571+ device_unlocked_at_least_once = true ;
572+ if (is_auto_reboot_timer_started) {
573+ auto_reboot_timer_set (0 );
574+ is_auto_reboot_timer_started = false ;
575+ LOG (INFO) << " auto_reboot: on_device_unlocked: stopped timer" ;
576+ } else {
577+ LOG (INFO) << " auto_reboot: on_device_unlocked: no started timer" ;
578+ }
579+ return PROP_SUCCESS;
580+ }
581+
582+ int duration_sec = atoi (value.c_str ()); // std::stoi can throw
583+ if (duration_sec <= 0 || (uint64_t ) duration_sec > (uint64_t ) std::numeric_limits<time_t >::max ()) {
584+ LOG (WARNING) << " auto_reboot: invalid prop value: " << value;
585+ return PROP_ERROR_INVALID_VALUE;
586+ }
587+
588+ if (device_unlocked_at_least_once) {
589+ if (is_auto_reboot_timer_started) {
590+ LOG (INFO) << " auto_reboot: timer is already started, ignored request to restart it;"
591+ << " requested timer duration: " << value << " seconds" ;
592+ } else {
593+ auto_reboot_timer_set ((time_t ) duration_sec);
594+ is_auto_reboot_timer_started = true ;
595+ }
596+ } else {
597+ LOG (INFO) << " auto_reboot: device was never unlocked, skipped setting timer" ;
598+ }
599+
600+ return PROP_SUCCESS;
601+ }
602+
527603// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*, or std::nullopt
528604// if asynchronous.
529605std::optional<uint32_t > HandlePropertySet (const std::string& name, const std::string& value,
@@ -566,6 +642,11 @@ std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::st
566642 return {PROP_SUCCESS};
567643 }
568644
645+ if (name == " sys.auto_reboot_ctl" ) {
646+ int res = auto_reboot_handle_property_set (value);
647+ return {res};
648+ }
649+
569650 return PropertySet (name, value, socket, error);
570651}
571652
@@ -1438,6 +1519,8 @@ static void PropertyServiceThread() {
14381519 LOG (FATAL) << result.error ();
14391520 }
14401521
1522+ auto_reboot_timer_init ();
1523+
14411524 while (true ) {
14421525 auto epoll_result = epoll.Wait (std::nullopt );
14431526 if (!epoll_result.ok ()) {
0 commit comments