11
11
import hashlib
12
12
import logging
13
13
import sqlite3
14
+ import time
14
15
15
16
import salt .utils .platform
16
17
import salt .utils .stringutils
@@ -36,7 +37,7 @@ def __virtual__():
36
37
return __virtualname__
37
38
38
39
39
- def install (app_id , enable = True ):
40
+ def install (app_id , enable = True , tries = 3 , wait = 10 ):
40
41
"""
41
42
Install a bundle ID or command as being allowed to use
42
43
assistive access.
@@ -47,20 +48,36 @@ def install(app_id, enable=True):
47
48
enabled
48
49
Sets enabled or disabled status. Default is ``True``.
49
50
51
+ tries
52
+ How many times to try and write to a read-only tcc. Default is ``True``.
53
+
54
+ wait
55
+ Number of seconds to wait between tries. Default is ``10``.
56
+
50
57
CLI Example:
51
58
52
59
.. code-block:: bash
53
60
54
61
salt '*' assistive.install /usr/bin/osascript
55
62
salt '*' assistive.install com.smileonmymac.textexpander
56
63
"""
57
- with TccDB () as db :
58
- try :
59
- return db .install (app_id , enable = enable )
60
- except sqlite3 .Error as exc :
61
- raise CommandExecutionError (
62
- "Error installing app({}): {}" .format (app_id , exc )
63
- )
64
+ num_tries = 1
65
+ while True :
66
+ with TccDB () as db :
67
+ try :
68
+ return db .install (app_id , enable = enable )
69
+ except sqlite3 .Error as exc :
70
+ if "attempt to write a readonly database" not in str (exc ):
71
+ raise CommandExecutionError (
72
+ "Error installing app({}): {}" .format (app_id , exc )
73
+ )
74
+ elif num_tries < tries :
75
+ time .sleep (wait )
76
+ num_tries += 1
77
+ else :
78
+ raise CommandExecutionError (
79
+ "Error installing app({}): {}" .format (app_id , exc )
80
+ )
64
81
65
82
66
83
def installed (app_id ):
@@ -175,22 +192,21 @@ def __init__(self, path=None):
175
192
def _check_table_digest (self ):
176
193
# This logic comes from https://github.com/jacobsalmela/tccutil which is
177
194
# Licensed under GPL-2.0
178
- with self .connection as conn :
179
- cursor = conn .execute (
180
- "SELECT sql FROM sqlite_master WHERE name='access' and type='table'"
181
- )
182
- for row in cursor .fetchall ():
183
- digest = hashlib .sha1 (row ["sql" ].encode ()).hexdigest ()[:10 ]
184
- if digest in ("ecc443615f" , "80a4bb6912" ):
185
- # Mojave and Catalina
186
- self .ge_mojave_and_catalina = True
187
- elif digest in ("3d1c2a0e97" , "cef70648de" ):
188
- # BigSur and later
189
- self .ge_bigsur_and_later = True
190
- else :
191
- raise CommandExecutionError (
192
- "TCC Database structure unknown for digest '{}'" .format (digest )
193
- )
195
+ cursor = self .connection .execute (
196
+ "SELECT sql FROM sqlite_master WHERE name='access' and type='table'"
197
+ )
198
+ for row in cursor .fetchall ():
199
+ digest = hashlib .sha1 (row ["sql" ].encode ()).hexdigest ()[:10 ]
200
+ if digest in ("ecc443615f" , "80a4bb6912" ):
201
+ # Mojave and Catalina
202
+ self .ge_mojave_and_catalina = True
203
+ elif digest in ("3d1c2a0e97" , "cef70648de" ):
204
+ # BigSur and later
205
+ self .ge_bigsur_and_later = True
206
+ else :
207
+ raise CommandExecutionError (
208
+ "TCC Database structure unknown for digest '{}'" .format (digest )
209
+ )
194
210
195
211
def _get_client_type (self , app_id ):
196
212
if app_id [0 ] == "/" :
@@ -200,14 +216,13 @@ def _get_client_type(self, app_id):
200
216
return 0
201
217
202
218
def installed (self , app_id ):
203
- with self .connection as conn :
204
- cursor = conn .execute (
205
- "SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'" ,
206
- (app_id ,),
207
- )
208
- for row in cursor .fetchall ():
209
- if row :
210
- return True
219
+ cursor = self .connection .execute (
220
+ "SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'" ,
221
+ (app_id ,),
222
+ )
223
+ for row in cursor .fetchall ():
224
+ if row :
225
+ return True
211
226
return False
212
227
213
228
def install (self , app_id , enable = True ):
@@ -234,9 +249,8 @@ def install(self, app_id, enable=True):
234
249
# indirect_object_identifier
235
250
# ),
236
251
# FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE);
237
- with self .connection as conn :
238
- conn .execute (
239
- """
252
+ self .connection .execute (
253
+ """
240
254
INSERT or REPLACE INTO access VALUES (
241
255
'kTCCServiceAccessibility',
242
256
?,
@@ -253,8 +267,9 @@ def install(self, app_id, enable=True):
253
267
0
254
268
)
255
269
""" ,
256
- (app_id , client_type , auth_value ),
257
- )
270
+ (app_id , client_type , auth_value ),
271
+ )
272
+ self .connection .commit ()
258
273
elif self .ge_mojave_and_catalina :
259
274
# CREATE TABLE IF NOT EXISTS "access" (
260
275
# service TEXT NOT NULL,
@@ -276,9 +291,8 @@ def install(self, app_id, enable=True):
276
291
# indirect_object_identifier
277
292
# ),
278
293
# FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE);
279
- with self .connection as conn :
280
- conn .execute (
281
- """
294
+ self .connection .execute (
295
+ """
282
296
INSERT or REPLACE INTO access VALUES(
283
297
'kTCCServiceAccessibility',
284
298
?,
@@ -294,23 +308,23 @@ def install(self, app_id, enable=True):
294
308
0
295
309
)
296
310
""" ,
297
- (app_id , client_type , auth_value ),
298
- )
311
+ (app_id , client_type , auth_value ),
312
+ )
313
+ self .connection .commit ()
299
314
return True
300
315
301
316
def enabled (self , app_id ):
302
317
if self .ge_bigsur_and_later :
303
318
column = "auth_value"
304
319
elif self .ge_mojave_and_catalina :
305
320
column = "allowed"
306
- with self .connection as conn :
307
- cursor = conn .execute (
308
- "SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'" ,
309
- (app_id ,),
310
- )
311
- for row in cursor .fetchall ():
312
- if row [column ]:
313
- return True
321
+ cursor = self .connection .execute (
322
+ "SELECT * from access WHERE client=? and service='kTCCServiceAccessibility'" ,
323
+ (app_id ,),
324
+ )
325
+ for row in cursor .fetchall ():
326
+ if row [column ]:
327
+ return True
314
328
return False
315
329
316
330
def enable (self , app_id ):
@@ -320,13 +334,13 @@ def enable(self, app_id):
320
334
column = "auth_value"
321
335
elif self .ge_mojave_and_catalina :
322
336
column = "allowed"
323
- with self .connection as conn :
324
- conn . execute (
325
- "UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'" . format (
326
- column
327
- ),
328
- ( 1 , app_id ),
329
- )
337
+ self .connection . execute (
338
+ "UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'" . format (
339
+ column
340
+ ),
341
+ ( 1 , app_id ),
342
+ )
343
+ self . connection . commit ( )
330
344
return True
331
345
332
346
def disable (self , app_id ):
@@ -336,23 +350,23 @@ def disable(self, app_id):
336
350
column = "auth_value"
337
351
elif self .ge_mojave_and_catalina :
338
352
column = "allowed"
339
- with self .connection as conn :
340
- conn . execute (
341
- "UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'" . format (
342
- column
343
- ),
344
- ( 0 , app_id ),
345
- )
353
+ self .connection . execute (
354
+ "UPDATE access SET {} = ? WHERE client=? AND service IS 'kTCCServiceAccessibility'" . format (
355
+ column
356
+ ),
357
+ ( 0 , app_id ),
358
+ )
359
+ self . connection . commit ( )
346
360
return True
347
361
348
362
def remove (self , app_id ):
349
363
if not self .installed (app_id ):
350
364
return False
351
- with self .connection as conn :
352
- conn . execute (
353
- "DELETE from access where client IS ? AND service IS 'kTCCServiceAccessibility'" ,
354
- ( app_id ,),
355
- )
365
+ self .connection . execute (
366
+ "DELETE from access where client IS ? AND service IS 'kTCCServiceAccessibility'" ,
367
+ ( app_id ,) ,
368
+ )
369
+ self . connection . commit ( )
356
370
return True
357
371
358
372
def __enter__ (self ):
0 commit comments