/
pylock.py
executable file
·900 lines (735 loc) · 28.1 KB
/
pylock.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ##############################################################################
# The contents of this file are subject to the PyTis Public License Version #
# 3.0 (the "License"); you may not use this file except in compliance with #
# the License. You may obtain a copy of the License at #
# #
# http://www.PyTis.com/License/ #
# #
# Copyright (c) 2019 Josh Lee #
# #
# Software distributed under the License is distributed on an "AS IS" basis, #
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License #
# for the specific language governing rights and limitations under the #
# License. #
# #
# @auto-generated by the PyTis Copyright Tool on 10:46 PM - 07 Jan, 2019 #
############################################################################## #
#XXX:TODO-NOW!
#Okay, it makes sense to have a thread, but I do not think I need the PyTis
#thread, I think it is way to complex. Look at this and come back.
"""pylock
======
NAME:
pylock
SYNOPSIS:
pylock [--options] [-H] [-v] [-D]
DESCRIPTION:
Three failed attemps, wait 1 minute.
If the user is not already root, then...
3 blank entries, switch username to root, to allow an administrator to walk
over and unlock the screen for an idiot who forgot their password.
IF there are 3 blank root entries, perhaps the idiot hit enter 3 times by
accident, 3 blank entries switches back to normal user.
Additionally, CTRL+C (KeyboardInterrupt) also will switch back to normal
user.
CODE:
UNDERSTANDING LOGGING and OUTPUT:
The way my PyTis scripts and programs work, I hardly ever use the print
command. Instead I just log something using a custom logging class. I have
four log levels: debug, info, warn and error. Messages may or may not make it
to the screen (as my logging class will print output as it logs) depending on
which arguments you pass into the program you are executing.
The current setup allows for 2 log files, to be written to the PyTis
configured log directory. If you do not use debug, then the log file used
will be the pytis_tools.log, if debugging is turned on, then the log file will
be {program_name}.log found in the same directory. In this program's case
that would be pybkup.log So if you use -D the output ends up in a different
log file, but this is just to keep debugging separate from normal logging. v-
--verbose is off by default by with this program, unless you use the action
'test' then it is on (unless you specify --quiet, then it stays off).
debug:
Not to log-file or to screen by default, but if turned on with the -D or
--debug flag, it will always end up in the debug log file (pybkup.log).
-D: print debug messages to log only
-v: alone does not print debug messages anywhere, because -D or --debug not
given
-DV: now debug messages are sent to their log file, and make it to the screen
info:
Print normal level information, always written to log (although which log is
being used depends on if you are using debugging, as stated above).
-v allows info messages to print to STDOUT, thus to the screen for you to
see.
-q or --quiet turns off -v
warn:
Warnings will always make it to which ever log is being used, and it will
always make it to the screen unless -q is passed in. If -q is passed in
then warnings are hidden from STDOUT, thus only show in log files.
-q suppresses warnings to STDOUT (the screen) but they will still end up in
whichever log this program is using.
error:
error messages always make it to the screen, no matter what; and are always
logged to the log file as well.
Notes:
To ensure there is no confusion, I want to specify that the log file is
chosen one time during the first stages of the program firing up, it does
not switch back and forth while running, nothing complex like that. Simply
this, all of my programs share pytis_tools.log unless they are in debug
mode. Then, since they are obviously going to spit out allot more
information, they write to their very own log file for that run. That way
they don't fill up and clutter the shared pytis_tools.log
'-DVq' confuses my little logging class. -q silences most messages, nothing
but errors make it to the screen, -D was passed in so the debug log is used
(pybkup.log) however logging level is set to verbose, with nothing to the
screen so what ends up happening is only info messages make it to the debug
log (debug messages hidden).
DEPENDENCIES:
"""
# ############################ DO NOT EDIT BELOW ############################ #
errors = []
# =============================================================================
# Begin Imports
# -----------------------------------------------------------------------------
# builtin
import optparse
import shlex
import pam
import time
import atexit
import os
import logging
import sys
import threading
import getpass
from subprocess import PIPE
import subprocess as sp
import platform
from pprint import pprint
#from cStringIO import StringIO
import signal
#import idle.idle as Idle
# This program needs to import PyTis(2) v4.1, which imports modules from the
# sub-package pylib, this program also needs to import from the sub-package
# cobj, pylib.cobj itself, has to import from the parent, pytis, which it
# can only do if the parent directory is a package, turning the parent (bin)
# into a package breaks importing pytis for this program in the first place
# and caused severe circular import errors. To fix this, we have to adjust the
# path.
# vvvvv XXX-TODO may not need this here, dunno, remove at end and try it. vvvvv
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),'..')))
# ^^^^^ XXX-TODO may not need this here, dunno, remove at end and try it. ^^^^^
#
# Internal
#
try:
#from pers import PyTis # Shared GPL/PPL License
import pytis as PyTis # Shared GPL/PPL License
from pylib import configobj as COBJ
except ImportError as e:
# We cannot go any further than this, we can't use the Parser or Logging tool
# to display these errors because those very tools are loaded from PyTis.
# Therefore, display errors now and exit with an errored exit code.
print("This program requires the PyTis Python library to run.")
print("You may download the PyTis library, or do an SVN checkout from:")
print("<https://sourceforge.net/projects/pytis/>")
print("This program should be installed in the bin directory of the PyTis library.")
print(str(e))
sys.exit(1)
is_debian = 'debian' in ' '.join(platform.dist()).lower() or 'ubuntu' in \
' '.join(platform.dist()).lower()
#
# Third-Party
#
# =============================================================================
# End Imports
# -----------------------------------------------------------------------------
__author__ = 'Josh Lee'
__copyright__ = 'PyTis.com'
__created__ = '08:32pm 04 Mar, 2018'
__curdir__ = os.path.abspath(os.path.dirname(__file__))
__version__ = '0.1'
tab=' '*4
# =============================================================================
# Begin HELPER Functions
# -----------------------------------------------------------------------------
SIGNALS_TO_NAMES_DICT = dict((getattr(signal, n), n) \
for n in dir(signal) if n.startswith('SIG') and '_' not in n )
class GetPasswordError (getpass.GetPassWarning): pass
#class GetPasswordError (getpass.GetPassWarning): pass
class LockScreen (object):
def getFullname(self, user):
return os.popen('getent passwd %s | cut -d ":" -f 5 | cut -d "," -f 1' % user).read()
def drawContext(self):
hostname = os.uname()[1].title()
username = str(getpass.getuser()).strip()
fullname = str(self.getFullname(username)).strip()
if username == fullname:
return "Screen used by <%s> on %s." % (username, hostname)
else:
return "Screen used by %s <%s> on %s." % (fullname, username, hostname)
def drawStart(self,tab=tab):
return """
%(tab)s ______________________________
%(tab)s
%(tab)s° PyTis ® Lock Screen © °
%(tab)s°______________________________°
""" % dict(tab=tab)
def drawWho(self, is_root=bool(getpass.getuser()=='root') ):
if is_root:
return "\nroot password >>> "
else:
return "\npassword >>> "
def getInput(self):
""" This function gets, and verifies the user's password, to unlock the
screen. This is the "Meat an' Tattors."
Three failed attemps, wait 1 minute.
If the user is not already root, then...
3 blank entries, switch username to root, to allow an administrator to walk
over and unlock the screen for an idiot who forgot their password.
IF there are 3 blank root entries, perhaps the idiot hit enter 3 times by
accident, 3 blank entries switches back to normal user.
Additionally, CTRL+C (KeyboardInterrupt) also will switch back to normal
user.
"""
# is the user signed in, already root? If so, we need to keep root, there
# is no reason to try to "switch" to root, or back to another user at any
# time
username = getpass.getuser()
started_as_root = bool(username=='root')
is_currently_root = started_as_root # pay attention to how I use this,
# it is unquiqe, and we will be flipping it back and forth, which kinda
# means to me that it is named wrong.
a = 11 # a=attempts
blank_answers = 0
# I am having one problem. It takes allot longer to validate a "blank"
# password, than it does one with a value (none empty string). Yet after
# we find out that the user's password is NOT blank, it takes a long time
# again, a second, and third time (etc). Why should it not be instant the
# second and additional times, if we now know that it isn't valid? Well, I
# am going to make a special function to handle just that. Below, you will
# find "authenticate" hmm, actually this will likely become it's
# doc-string.
_elist = []
def getPass(prompt='Password: ', use_litteral=True):
""" During the writing of this program, and perhaps later during debug, I
need to turn stdout on, per-say, I need to see specifically what keys
were typed, and when blanks (just hitting return) occured. Thus, this
will NORMALLY just use, getpass.getpass(), passing the prompt in as the
arguemnt. However, when I tell this NOT to use the littlal
(getpass.getpass) then I will have it just use PyTis.getInput, or
something along those lines...
Lastly, this should be named "getpass" to follow proper naming
convensions. It is a function, not a method. However, to ensure it
doesn't get confused with or override the imported getpass, I am naming
it getPass or get_pass... hmm. well I don't want it name it as a variable
should be named, so getPass it is.
"""
try:
if use_litteral:
retval= getpass.getpass(str(prompt).strip())
else:
retval= raw_input("plain-text > %s" %str(prompt).strip())
log.debug('-'*80)
except KeyboardInterrupt as e:
if bool(self.opts.debug or self.opts.dry_run):
log.info("\nKeyboardInterrupt permitted in [-D/--debug] or " \
"[-d/--dry-run] modes.")
log.debug("Keyboard-Interrupt, bye!")
raise PyTis.QuitNow(e)
else:
print ("\n KeyboardInterrupt not permitted. \n")
return getPass(prompt, use_litteral)
return retval
def authenticate(username, password, elist=_elist):
""" elist will be used to store an immutable dict (from the
function's point of view, it won't be reset each time the function is
called).
"""
global log
# service: PAM service to authenticate against, defaults to 'login'
service='login'
# login service seems to return faster,
if username=='root':
service='passwd'
log.debug("authenticate, authenticating: %s " % username)
if not str(password).strip(): # password IS blank
log.debug("password is blank!")
if username in elist:
return False
else:
elist.append(username)
if pam.authenticate(username, password):
return True
else:
return False # didn't have this, and it authenticated a second time
# below, causing the first instance of a blank password to take
# twice as long to authenticate, duh!
else:
if pam.authenticate(username, password):
return True
else:
return False
while True:
log.debug('start of loop')
if a > 10:
PyTis.clearScreen()
print( self.drawStart() )
print( self.drawContext() )
a=0
a+=1
log.debug("a count: %s" % str(int(a)))
log.debug("blank count: %s" % str(int(blank_answers)))
if not started_as_root and blank_answers == 3:
is_currently_root = not is_currently_root
if is_currently_root: user = 'root'
else: user = username
log.debug("username: %s " % user)
blank_answers = 0
# time to switch
if is_currently_root: password = getPass(self.drawWho(True),
not bool(self.opts.debug and self.opts.dry_run))
else:
password = getPass(self.drawWho(False),
not bool(self.opts.debug and self.opts.dry_run))
else:
if is_currently_root: user = 'root'
else: user = username
log.debug("username: %s " % user)
# blank answers or not, we started as root, thus will remain root.
password = getPass(self.drawWho(is_currently_root),
not bool(self.opts.debug and self.opts.dry_run))
if password == str('').strip():
# the user entered a blank password ( by blindly pressing only the
# enter key str('') ), let us increment the counter now.
blank_answers +=1
else:
# fairly obvious, but just incase,...
# there was a password entered (not blank (user just blindly pressing
# return) ), thus, it wasn't a blank one, thus, reset the counter.
blank_answers = 0 # clear the counter
# not a blank answer, let's check to see if it is right.
if authenticate(user, password):
return True
else:
if password:
print("Invalid Password.\n")
else:
print("%s's PASSWORD is required to unlock the screen." % user)
continue
def run(self):
return self.getInput()
def getInput2(question, helptext='No help for this command',example_or_hint=None,default=None,required=False):
"""
Full Name - press Enter for default: root
if helptext:
if required:
Full Name [i.e. John Smith (required)]:
else:
Full Name [i.e. John Smith (or leave blank)]:
else:
if required:
Full Name (required):
else:
Full Name:
"""
if helptext == 'No help for this command' or not helptext.strip():
h=''
else:
h = '/?'
if example_or_hint:
if required:
txt = '%s %s%s ' % (question,example_or_hint,h)
else:
txt = '%s %s (or leave blank)%s ' % (question,example_or_hint,h)
else:
if default and required and not example_or_hint:
example_or_hint='(default "%s")' % default
txt = '%s %s%s ' % (question,example_or_hint,h)
try:
res = raw_input("%s>>> " % (txt))
except (KeyboardInterrupt,EOFError) as e:
print("\nInvalid input, press 'q' to quit or 'h' for help.")
return getInput(question,helptext,example_or_hint,default,required)
if not res.strip() and required:
if default:
__option_always__ = [False]
if getInputYN('Use default: %s?' % (default),
'This will set the %s to the system default of "%s"' % (question,default),
__input_options__,[]):
return default
else:
if not example_or_hint:
example_or_hint='(default "%s")' % default
return getInput(question,helptext,example_or_hint,default,required)
else:
print("\n")
print('Nothing entered "%s" is required, please try again.' % question)
print("\n")
if not example_or_hint:
example_or_hint='(default "%s")' % default
return getInput(question,helptext,example_or_hint,default,required)
if res.strip().lower() in ['h','?']:
print("\nHELP (q to quit)" )
print("\n\t? or "+'"h"'+" for this help")
print("\ty for Yes")
print("\tn for No")
if helptext:
if required:
r="\t\tREQUIRED -\n "
else:
r=''
print("\n%s" % fixWidth("%s%s"%(r,helptext)))
return getInput(question,helptext,example_or_hint,default,required)
if res.strip().lower() == 'q':
raise QuitNow(question)
else:
return res
class LockScreenThread(PyTis.MyThread):
def __init__(self):
''' So, "Start" could be Confused with Run, but "Start" fires the process
up, and gets the wheels turning, then will actually call "Run". Once
"Start" is called, it next calls lowercase "start" to do anything that
should happen when the program is started. Then, it calls "Run" which is
the threaded function, that may be called multiple times.
"_run" is only called, when the programmer forgets to assign any callbacks.
Remmember, "Run" is the thread. It can call "_stop", if it needs to exit.
"_stop" calls "stop" which can be overridden, it's purpose is last minute
cleanup, for example, a user presses CTRL+c, for a KeyboardInterrupt, log
files need closing, etc.
"Stop" is called by this program, but another instance, that can look up a
deamon finding it's PID, and sending a SIGTERM.
'''
global log
threading.Thread.__init__(self, target=self.Run, name='LockScreen-Thread')
self.keep_going=True
if log is not None:
self.setLogFile(log)
atexit.register(self.stop)
def buildAttrs(self, opts):
try:
self.niceness = opts.niceness
except AttributeError as e:
self.niceness = self.default_niceness
try:
self.ioniceness_class = opts.ioniceness_class
except AttributeError as e:
self.ioniceness_class = self.default_ioniceness_class
try:
self.ioniceness = opts.ioniceness
except AttributeError as e:
self.ioniceness = self.default_ioniceness
try:
self.frequency = int(opts.frequency)
except (AttributeError, NameError, ValueError) as e:
self.frequency = self.default_frequency
def service(self, opts):
''' alias for self.control with the "action" pulled out of opts and passed
in seperately
'''
try:
self.action = opts.action
except AttributeError as e:
if type(opts) is type(str()) and opts.lower() in ('start','stop',
'restart','status'):
self.action=opts
else:
raise ProgrammerError("optparse action is missing, to " \
"use MyThread.service optparse must have a valid action " \
"(start,stop,restart,status)")
self.buildAttrs(opts)
return self.control(self.action)
def _run(self):
self.log.warn('running placeholder until callbacks are set')
#PyTis.clearScreen()
def daemonize(self):
return True
def _stop(self):
self.keep_going=False
self.stop()
def start(self):
''' You should override this method when you subclass Process.
It will be called before the process will be runned via when you call caplital
Start. '''
#self.log.debug('override start')
#pass
global log
# Okay, so the first thing we will want to do, is clear the screen out,
# make it black.
self.buildAttrs(self.opts)
PyTis.clearScreen()
def stop(self):
''' CALLED atexit REGISTERED in INIT, also called by _stop, which is called
in Run, when keep_going is found to be False.
'''
"""
You should override this method when you subclass Process.
It will be called after the process has been stopped or interupted by
signal.SIGTERM"""
#self.log.debug('override stop')
pass
def Start(self):
stderr = sys.stderr
old_pid = self.pidfile.validate()
if old_pid:
self.log.error("Error during service start:\n " \
"Already running on PID %s (or pid file '%s' is stale)" % \
(old_pid, self.pidfile.pidfile))
return
# Start the service
self.log.info("starting the %s service now" % (self.parent_name))
if self.daemonize():
try:
self.pidfile.create()
except RuntimeError as err:
# *IMPORTANT* No matter what, this will not make it to the screen.
# Even print statements won't make it to the screen from here.
# This is already within the spawned child process with no open pipes
# to the parent. The best we can do is log.
self.log.error("Error during service start: %s" % str(err))
return
atexit.register(self.remove_pid)
self.start()
try:
self.keep_going = True
self.running = True
# thread.start_new_thread(self.Run, None, self.opts)
t=threading.Thread(None,self.Run,None)
# self.Run()
t.start()
#t.opts=self.opts
# t.join()
except Exception as e:
self.keep_going=False
self.running=False
self.log.error(e)
#print >> sys.stderr, e
#sys.stderr.write(str(e))
raise Exception(e)
return
self.log.info('%s service started' % self.parent_name)
else:
self.log.info("Success, all done")
return
def Stop(self):
pid = self.pidfile.validate()
if not pid:
self.log.error("pidfile %s does not exist. The %s service is not " \
"running." % (self.pidfile.pidfile, self.parent_name))
return # not an error in a restart
# Try killing the service process
try:
while 1:
os.kill(pid, signal.SIGTERM)
time.sleep(0.1)
except OSError as err:
err = str(err)
if err.find("No such process") > 0:
self.pidfile.unlink()
else:
self.log.error('Error during service stop, %s' % str(err))
raise OSError(err)
self.log.info('stopping %s [%s] service.' %(self.parent_name, pid))
self.log.debug('service [%s] was stopped by SIGTERM signal' % pid)
#def remove_pid(self):
# if self.pidfile.validate(): self.pidfile.unlink()
def Run(self):
i=0
os.nice(self.niceness)
while self.keep_going:
if not self.callbacks:
self._run()
else:
if self.frequency or ( not self.frequency and not i):
for v in self.callbacks:
callback = v.getCallback()
args = v.getArgs()
kwargs = v.getKwArgs()
try:
callback(*args,**kwargs)
except (KeyboardInterrupt, QuitNow) as e:
print("\nbye!")
self.keep_going = False
self._stop()
self.running=False
return
except Exception as e:
self.log.error("Some error occured.")
type_,value_,traceback_ = sys.exc_info()
self.log.debug("type: %s" % type_)
self.log.debug("type2: %s" %type(e))
self.log.debug("value: %s" % value_)
for tb_line in traceback.format_tb(traceback_):
self.log.debug(tb_line)
self.log.error(str(e))
if not self.frequency and not i:
self._stop()
self.running=False
return
if self.frequency:
time.sleep(self.frequency)
try:
if self.opts.debug:
i+=1
if i > 2:
self._stop()
self.running=False
return
except (AttributeError, NameError) as e:
pass
def run(opts, config):
""" Okay, I know I can just keep this in a function, K.I.S.S., and I hope I
am not horribly breaking the KISS rule, but I am usually going to be calling
this with the need for it to be in a thread. Therefore, I am going to write
it into a class to make it easier in the future for threading.
"""
lock_screen = LockScreen()
lock_screen.opts = opts
lock_screen.run()
return
lock_screen2 = LockScreenThread()
lock_screen2.opts = opts
lock_screen2.daemonize
lock_screen2.Start()
# -----------------------------------------------------------------------------
# End HELPER Functions
# =============================================================================
def main():
"""usage: pylock [--PAM] [-Dv] (example: pylock)
"""
global __configdir__, errors, log
PyTis.__option_always__ = [True]
help_dict = dict(version=__version__,
author=__author__,
created=__created__,
copyright=__copyright__)
parser = PyTis.MyParser()
parser.extra_txt = "\n\n%s\n" % run.__doc__ + """
EXAMPLES:
pylock
SEE ALSO:
vlock
/local/bin/lck or /usr/bin/lock
COPYRIGHT:
%(copyright)s
AUTHOR:
%(author)s
CHANGE LOG:
v.1 MINOR CHANGE
Ran importnanny and removed un-needed imports.
CREATED:
%(created)s
HISTORY:
Original Author
TODO:
Screen Savers... Completing this will bring pylock to 100%%, and
thus, version 1.
VERSION:
%(version)s
""" % help_dict
parser.formatter.format_description = lambda s:s
if '--help' in sys.argv:
parser.set_description(__doc__)
helpishere=True # to determine help mode (short or full)
# "Only tell the user "
dry_run_help = 'Enable the dry-run flag (-d/--dry-run) in order to test ' \
'what this script would do, if ran normally. Dry Run does not ' \
' re-cache, send emails, run commands, or run sql statements (see ' \
'"Configuring Actions" section of full help text), and thus in ' \
'repitition would continue to report that a file has changed. ' \
'If the file has changed since the previous non-dry-run caching of ' \
'said URL, output will reflect such. If a URL has not changed since ' \
'the last cached URL, then the output will state there is no change. ' \
'Dry Run [-d/--dry-run] is great for when an adminstrator (or user) ' \
'wishes to just test and see if anything has changed, without any ' \
'actions taking place. Generally the actions are fired off when ' \
'this program is callef rom the cron jobs.`$' \
'Lastly, using the [-d/--dry-run] flag, implies verbosity, and unless ' \
'the [-q/--quiet] flag is provided, this program will default to ' \
'verbose mode.`$'
else:
parser.set_description('')
helpishere=False # to determine help mode (short or full)
dry_run_help = 'Reports changes as STDOUT, without executing any '\
'configured actions, or caching. Implies "verbosity."' \
' *(use "--help" for more details)`$'
runtime = optparse.OptionGroup(parser, "-- RUNTIME ARGUMENTS")
runtime.add_option("-d", "--dry-run", action="store_true", #type="bool",
default=False, dest='dry_run',
help=dry_run_help)
#help=optparse.SUPPRESS_HELP)
parser.add_option_group(runtime)
# -------------------------------------------------------------------------
# variable setting
vars = optparse.OptionGroup(parser, "-- CONFIGURATION SETTINGS")
parser.add_option_group(vars)
# ----------------------------
dbgroup = optparse.OptionGroup(parser, "-- DEBUG")
dbgroup.add_option("-D", "--debug", action="store_true",
default=False, dest='debug',
help="Enable debugging`$")
# This is a little trick to tell if the user entered the -v/--verbose flag.
# We want verbosity on by default, but we also want to know if the user
# entered it for debug items, and providing end messages vs informed output.
dbgroup.add_option("", "--totaly-verbose", action="store_true",
default=False, dest='totally_verbose',
help=optparse.SUPPRESS_HELP)
dbgroup.add_option("-v", "--verbose", action="store_true",
default=False, dest='verbose',
help="Be more Verbose (make lots of noise). Off by default, " \
"unless in dry-run.`$")
dbgroup.add_option("-q", "--quiet", action="store_true",
default=False, dest='quiet',
help="be vewwy quiet (I'm hunting wabbits). On by default, " \
"unless in dry-run.`$")
dbgroup.add_option("-V", "--version", action="store_true",
default=False, dest='version',
help="Display Version")
parser.add_option_group(dbgroup)
# ----------------------------
(opts, args) = parser.parse_args()
if opts.verbose:
opts.totally_verbose = True
if opts.quiet: opts.verbose = False
if opts.dry_run and not opts.quiet: opts.verbose = True
if not opts.quiet: opts.verbose = True # Defaults to Verbose
#if not opts.verbose: opts.quiet = True # Defaults to Quiet
if opts.debug:
main.__doc__ = "%s\n\n CONFIG FILE: %s" % (main.__doc__, \
os.path.abspath('none.txt'))
else:
pass
parser.set_usage(main.__doc__)
old_version = opts.version
opts.version = True
log = PyTis.set_logging(opts, os.path.basename(sys.argv[0]))
opts.version = old_version
if opts.version:
return PyTis.version(__version__)
config={}
if not opts.quiet and len(args) and not errors:
return parser.print_usage()
elif not errors:
try:
run(opts, config)
except KeyboardInterrupt as e:
log.debug("Keyboard-Interrupt, bye!")
if not opts.quiet: log.info("\nbye!")
return 0
except PyTis.QuitNow as e:
log.debug(e)
if not opts.quiet: log.info("\nbye!")
print("\n")
return 0
else:
if opts.totally_verbose: log.info("Done.")
return 0
else:
parser.print_usage()
if errors:
log.error(str("\n".join(errors)))
return parser.print_help(errors)
parser.print_help("ERROR: Unknown, but invalid input.")
sys.exit(1)
if __name__ == '__main__':
sys.exit(main())