3535#include "string-util.h"
3636#include "udev-util.h"
3737#include "util.h"
38+ #include "list.h"
3839
40+ /* Note that any write is delayed until exit and the rfkill state will not be
41+ * stored for rfkill indices that disappear after a change. */
3942#define EXIT_USEC (5 * USEC_PER_SEC)
4043
44+ typedef struct write_queue_item {
45+ LIST_FIELDS (struct write_queue_item , queue );
46+ int rfkill_idx ;
47+ char * file ;
48+ int state ;
49+ } write_queue_item ;
50+
51+ static void write_queue_item_free (struct write_queue_item * item )
52+ {
53+ assert (item );
54+
55+ free (item -> file );
56+ free (item );
57+ }
58+
4159static const char * const rfkill_type_table [NUM_RFKILL_TYPES ] = {
4260 [RFKILL_TYPE_ALL ] = "all" ,
4361 [RFKILL_TYPE_WLAN ] = "wlan" ,
@@ -259,12 +277,30 @@ static int load_state(
259277 return 0 ;
260278}
261279
262- static int save_state (
280+ static void save_state_queue_remove (
281+ struct write_queue_item * * write_queue ,
282+ int idx ,
283+ char * state_file ) {
284+
285+ struct write_queue_item * item , * tmp ;
286+
287+ LIST_FOREACH_SAFE (queue , item , tmp , * write_queue ) {
288+ if ((state_file && streq (item -> file , state_file )) || idx == item -> rfkill_idx ) {
289+ log_debug ("Canceled previous save state of '%s' to %s." , one_zero (item -> state ), item -> file );
290+ LIST_REMOVE (queue , * write_queue , item );
291+ write_queue_item_free (item );
292+ }
293+ }
294+ }
295+
296+ static int save_state_queue (
297+ struct write_queue_item * * write_queue ,
263298 int rfkill_fd ,
264299 struct udev * udev ,
265300 const struct rfkill_event * event ) {
266301
267302 _cleanup_free_ char * state_file = NULL ;
303+ struct write_queue_item * item ;
268304 int r ;
269305
270306 assert (rfkill_fd >= 0 );
@@ -274,16 +310,69 @@ static int save_state(
274310 r = determine_state_file (udev , event , & state_file );
275311 if (r < 0 )
276312 return r ;
313+ save_state_queue_remove (write_queue , event -> idx , state_file );
277314
278- r = write_string_file (state_file , one_zero (event -> soft ), WRITE_STRING_FILE_CREATE |WRITE_STRING_FILE_ATOMIC );
315+ item = new0 (struct write_queue_item , 1 );
316+ if (!item )
317+ return - ENOMEM ;
318+
319+ item -> file = state_file ;
320+ item -> rfkill_idx = event -> idx ;
321+ item -> state = event -> soft ;
322+ state_file = NULL ;
323+
324+ LIST_APPEND (queue , * write_queue , item );
325+
326+ return 0 ;
327+ }
328+
329+ static int save_state_cancel (
330+ struct write_queue_item * * write_queue ,
331+ int rfkill_fd ,
332+ struct udev * udev ,
333+ const struct rfkill_event * event ) {
334+
335+ _cleanup_free_ char * state_file = NULL ;
336+ int r ;
337+
338+ assert (rfkill_fd >= 0 );
339+ assert (udev );
340+ assert (event );
341+
342+ r = determine_state_file (udev , event , & state_file );
343+ save_state_queue_remove (write_queue , event -> idx , state_file );
279344 if (r < 0 )
280- return log_error_errno ( r , "Failed to write state file %s: %m" , state_file ) ;
345+ return r ;
281346
282- log_debug ("Saved state '%s' to %s." , one_zero (event -> soft ), state_file );
283347 return 0 ;
284348}
285349
350+ static int save_state_write (struct write_queue_item * * write_queue ) {
351+ struct write_queue_item * item , * tmp ;
352+ int result = 0 ;
353+ bool error_logged = false;
354+ int r ;
355+
356+ LIST_FOREACH_SAFE (queue , item , tmp , * write_queue ) {
357+ r = write_string_file (item -> file , one_zero (item -> state ), WRITE_STRING_FILE_CREATE |WRITE_STRING_FILE_ATOMIC );
358+ if (r < 0 ) {
359+ result = r ;
360+ if (!error_logged ) {
361+ log_error_errno (r , "Failed to write state file %s: %m" , item -> file );
362+ error_logged = true;
363+ } else
364+ log_warning_errno (r , "Failed to write state file %s: %m" , item -> file );
365+ } else
366+ log_debug ("Saved state '%s' to %s." , one_zero (item -> state ), item -> file );
367+
368+ LIST_REMOVE (queue , * write_queue , item );
369+ write_queue_item_free (item );
370+ }
371+ return result ;
372+ }
373+
286374int main (int argc , char * argv []) {
375+ LIST_HEAD (write_queue_item , write_queue );
287376 _cleanup_udev_unref_ struct udev * udev = NULL ;
288377 _cleanup_close_ int rfkill_fd = -1 ;
289378 bool ready = false;
@@ -294,6 +383,8 @@ int main(int argc, char *argv[]) {
294383 return EXIT_FAILURE ;
295384 }
296385
386+ LIST_HEAD_INIT (write_queue );
387+
297388 log_set_target (LOG_TARGET_AUTO );
298389 log_parse_environment ();
299390 log_open ();
@@ -403,11 +494,12 @@ int main(int argc, char *argv[]) {
403494
404495 case RFKILL_OP_DEL :
405496 log_debug ("An rfkill device has been removed with index %i and type %s" , event .idx , type );
497+ (void ) save_state_cancel (& write_queue , rfkill_fd , udev , & event );
406498 break ;
407499
408500 case RFKILL_OP_CHANGE :
409501 log_debug ("An rfkill device has changed state with index %i and type %s" , event .idx , type );
410- (void ) save_state ( rfkill_fd , udev , & event );
502+ (void ) save_state_queue ( & write_queue , rfkill_fd , udev , & event );
411503 break ;
412504
413505 default :
@@ -419,5 +511,7 @@ int main(int argc, char *argv[]) {
419511 r = 0 ;
420512
421513finish :
514+ (void ) save_state_write (& write_queue );
515+
422516 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS ;
423517}
0 commit comments