1
- #!/usr/bin/python3
1
+ #!/usr/bin/python3 -u
2
2
'Program to run plugins to inhibit system sleep/suspend.'
3
3
# Requires python 3.6+
4
4
# Mark Blakeney, Jul 2020.
5
5
6
- # Standard packages
7
- import sys
8
6
import argparse
7
+ import asyncio
8
+ import shlex
9
9
import subprocess
10
- import threading
10
+ import sys
11
11
import time
12
- import shlex
13
12
from pathlib import Path
14
13
15
14
# Return code which indicates plugin wants to inhibit suspend
22
21
'systemd-inhibit' ,
23
22
)
24
23
25
- def gettime (conf , field , default = None ):
26
- 'Read time value from given conf.'
27
- val = conf .get (field , default )
28
- if val is None :
29
- return None
30
-
24
+ def conv_to_secs (val ):
25
+ 'Convert given time string to float seconds'
31
26
if isinstance (val , str ):
32
27
if val .endswith ('s' ):
33
- num = float (val [:- 1 ]) / 60
34
- elif val .endswith ('m' ):
35
28
num = float (val [:- 1 ])
36
- elif val .endswith ('h ' ):
29
+ elif val .endswith ('m ' ):
37
30
num = float (val [:- 1 ]) * 60
31
+ elif val .endswith ('h' ):
32
+ num = float (val [:- 1 ]) * 60 * 60
38
33
else :
39
- sys .exit (f'Invalid time value " { field } : { val } ".' )
34
+ sys .exit (f'Invalid time string " { val } ".' )
40
35
else :
41
- num = float (val )
36
+ # Default time entry is minutes
37
+ num = float (val ) * 60
42
38
43
39
return num
44
40
45
41
class Plugin :
46
42
'Class to manage each plugin'
47
- loglock = threading .Lock ()
48
- threads = []
49
-
50
- def __init__ (self , index , prog , progname , def_period , def_period_on ,
43
+ def __init__ (self , index , prog , progname , period , period_on ,
51
44
def_what , conf , plugin_dir , inhibitor_prog ):
52
45
'Constructor'
53
46
pathstr = conf .get ('path' )
@@ -69,15 +62,15 @@ def __init__(self, index, prog, progname, def_period, def_period_on,
69
62
if not path .exists ():
70
63
sys .exit (f'{ self .name } : "{ path } " does not exist' )
71
64
72
- period = gettime (conf , 'period' )
73
- if period is None :
74
- period = def_period
75
- period_on_def = def_period_on
76
- else :
77
- period_on_def = period
65
+ period_str = conf .get ('period' , period )
66
+ self .period = conv_to_secs (period_str )
67
+
68
+ period_on_str = conf .get ('period_on' , period_on )
69
+ period_on = conv_to_secs (period_on_str )
70
+ if period_on > self .period :
71
+ period_on = self .period
72
+ period_on_str = period_str
78
73
79
- period_on = gettime (conf , 'period_on' , period_on_def )
80
- self .period = period * 60
81
74
self .is_inhibiting = None
82
75
83
76
cmd = str (path )
@@ -95,46 +88,31 @@ def __init__(self, index, prog, progname, def_period, def_period_on,
95
88
# run the plugin in a loop which keeps the inhibit on while the
96
89
# inhibit state is returned.
97
90
self .icmd = shlex .split (f'{ inhibitor_prog } { what } --who="{ progname } " '
98
- f'--why="{ self .name } " { prog } -s { period_on * 60 } -i "{ cmd } "' )
91
+ f'--why="{ self .name } " { prog } -s { period_on } -i "{ cmd } "' )
99
92
100
- per = round (period , 3 )
101
- per_on = round (period_on , 3 )
102
- print (f'{ self .name } [{ path } ] configured @ { per } /{ per_on } minutes' )
93
+ print (f'{ self .name } [{ path } ] configured @ { period_str } /{ period_on_str } ' )
103
94
104
- # Each plugin periodic check runs in it's own thread
105
- thread = threading .Thread (target = self .run )
106
- thread .daemon = True
107
- thread .start ()
108
- self .threads .append (thread )
109
-
110
- def run (self ):
111
- 'Worker function which runs it its own thread'
95
+ async def run (self ):
96
+ 'Worker function which runs as a asyncio task for each plugin'
112
97
while True :
113
- res = subprocess .run (self .cmd )
114
- while res .returncode == SUSP_CODE :
98
+ proc = await asyncio .create_subprocess_exec (* self .cmd )
99
+ await proc .wait ()
100
+
101
+ while proc .returncode == SUSP_CODE :
115
102
if not self .is_inhibiting :
116
103
self .is_inhibiting = True
117
- self . log (f'{ self .name } is inhibiting '
118
- f'suspend (return={ res .returncode } )' )
104
+ print (f'{ self .name } is inhibiting '
105
+ f'suspend (return={ proc .returncode } )' )
119
106
120
- res = subprocess .run (self .icmd )
107
+ proc = await asyncio .create_subprocess_exec (* self .icmd )
108
+ await proc .wait ()
121
109
122
110
if self .is_inhibiting is not False :
123
111
self .is_inhibiting = False
124
- self .log (f'{ self .name } is not inhibiting '
125
- f'suspend (return={ res .returncode } )' )
126
-
127
- time .sleep (self .period )
128
-
129
- @classmethod
130
- def log (cls , msg ):
131
- 'Thread locked print()'
132
- if not msg .endswith ('\n ' ):
133
- msg += '\n '
112
+ print (f'{ self .name } is not inhibiting '
113
+ f'suspend (return={ proc .returncode } )' )
134
114
135
- # Use a lock so thread messages do not get interleaved
136
- with cls .loglock :
137
- sys .stdout .write (msg )
115
+ await asyncio .sleep (self .period )
138
116
139
117
def init ():
140
118
'Program initialisation'
@@ -214,22 +192,21 @@ def init():
214
192
plugin_dir = args .plugin_dir or conf .get ('plugin_dir' , plugin_dir )
215
193
216
194
# Get some global defaults
217
- period = gettime ( conf , 'period' , 5 )
218
- period_on = gettime ( conf , 'period_on' , period )
195
+ period = conf . get ( 'period' , '5m' )
196
+ period_on = conf . get ( 'period_on' , period )
219
197
what = conf .get ('what' )
220
198
221
199
# Iterate to create each configured plugins
222
- for index , plugin in enumerate ( plugins , 1 ):
223
- Plugin ( index , prog , progname , period , period_on , what , plugin ,
224
- plugin_dir , inhibitor_prog )
200
+ return [ Plugin ( index , prog , progname , period , period_on , what , plugin ,
201
+ plugin_dir , inhibitor_prog )
202
+ for index , plugin in enumerate ( plugins , 1 )]
225
203
226
- def main ():
204
+ async def main ():
227
205
'Main entry'
228
- init ()
206
+ tasks = init ()
229
207
230
- # Wait for each thread to finish (i.e. wait forever)
231
- for thread in Plugin .threads :
232
- thread .join ()
208
+ # Wait for each plugin task to finish (i.e. wait forever)
209
+ await asyncio .gather (* (t .run () for t in tasks ))
233
210
234
211
if __name__ == '__main__' :
235
- sys . exit (main ())
212
+ asyncio . run (main (), debug = True )
0 commit comments