3535import time
3636import tzlocal
3737import threading
38+ import psutil
3839
3940import speakreader
4041from speakreader import logger , config , SpeakReader
@@ -51,8 +52,6 @@ def main():
5152
5253 DAEMON = False
5354 NOFORK = False
54- CREATEPID = False
55- PIDFILE = None
5655
5756 QUIET = False
5857 VERBOSE = False
@@ -67,6 +66,7 @@ def main():
6766 PLATFORM_PROCESSOR = processor ()
6867 PLATFORM_MACHINE = machine ()
6968 PLATFORM_IS_64BITS = sys .maxsize > 2 ** 32
69+ SYS_PLATFORM = sys .platform
7070
7171 # Fixed paths to application
7272 if hasattr (sys , 'frozen' ):
@@ -76,19 +76,26 @@ def main():
7676
7777 PROG_DIR = os .path .dirname (FULL_PATH )
7878
79- SYS_PLATFORM = sys .platform
80- SYS_ENCODING = None
79+ # Ensure only one instance of SpeakReader running.
80+ PIDFILE = os .path .join (PROG_DIR , 'pidfile' )
81+ myPid = os .getpid ()
82+ if os .path .exists (PIDFILE ) and os .path .isfile (PIDFILE ):
83+ for p in psutil .process_iter ():
84+ if 'python' in p .name () and p .pid != myPid :
85+ for f in p .open_files ():
86+ if f .path == PIDFILE :
87+ logger .error ("SpeakReader is already Running. Exiting." )
88+ sys .exit (0 )
89+
90+ myPidFile = open (PIDFILE , 'w+' )
91+ myPidFile .write (str (myPid ))
92+ myPidFile .flush ()
8193
8294 try :
8395 locale .setlocale (locale .LC_ALL , "" )
84- SYS_LANGUAGE , SYS_ENCODING = locale .getdefaultlocale ()
8596 except (locale .Error , IOError ):
8697 pass
8798
88- # for OSes that are poorly configured I'll just force UTF-8
89- if not SYS_ENCODING or SYS_ENCODING in ('ANSI_X3.4-1968' , 'US-ASCII' , 'ASCII' ):
90- SYS_ENCODING = 'UTF-8'
91-
9299 try :
93100 SYS_TIMEZONE = str (tzlocal .get_localzone ())
94101 SYS_UTC_OFFSET = datetime .datetime .now (pytz .timezone (SYS_TIMEZONE )).strftime ('%z' )
@@ -116,8 +123,6 @@ def main():
116123 '--config' , help = 'Specify a config file to use' )
117124 parser .add_argument (
118125 '--nolaunch' , action = 'store_true' , help = 'Prevent browser from launching on startup' )
119- parser .add_argument (
120- '--pidfile' , help = 'Create a pid file (only relevant when running as a daemon)' )
121126 parser .add_argument (
122127 '--nofork' , action = 'store_true' , help = 'Start SpeakReader as a service, do not fork when restarting' )
123128
@@ -148,35 +153,6 @@ def main():
148153 NOFORK = True
149154 logger .info ("SpeakReader is running as a service, it will not fork when restarted." )
150155
151- if args .pidfile :
152- PIDFILE = str (args .pidfile )
153-
154- # If the pidfile already exists, SpeakReader may still be running, so EXIT
155- if os .path .exists (PIDFILE ):
156- try :
157- with open (PIDFILE , 'r' ) as fp :
158- pid = int (fp .read ())
159- os .kill (pid , 0 )
160- except IOError as e :
161- raise SystemExit ("Unable to read PID file: %s" , e )
162- except OSError :
163- logger .warn ("PID file '%s' already exists, but PID %d is not running. Ignoring PID file." %
164- (PIDFILE , pid ))
165- else :
166- # The pidfile exists and points to a live PID. SpeakReader may still be running, so exit.
167- raise SystemExit ("PID file '%s' already exists. Exiting." % PIDFILE )
168-
169- # The pidfile is only useful in daemon mode, make sure we can write the file properly
170- if DAEMON :
171- CREATEPID = True
172- try :
173- with open (PIDFILE , 'w' ) as fp :
174- fp .write ("pid\n " )
175- except IOError as e :
176- raise SystemExit ("Unable to write PID file: %s" , e )
177- else :
178- logger .warn ("Not running in daemon mode. PID file creation disabled." )
179-
180156 # Determine which data directory and config file to use
181157 if args .datadir :
182158 DATA_DIR = args .datadir
@@ -246,7 +222,7 @@ def main():
246222 CONFIG .RECORDINGS_FOLDER , os .path .join (DATA_DIR , 'recordings' ), 'recordings' )
247223
248224 if DAEMON :
249- daemonize (CREATEPID , PIDFILE )
225+ daemonize (myPidFile )
250226
251227 # Store the original umask
252228 UMASK = os .umask (0 )
@@ -295,9 +271,8 @@ def main():
295271
296272 SR .shutdown (restart = restart , update = update , checkout = checkout )
297273
298- if CREATEPID :
299- logger .info ("Removing pidfile %s" , PIDFILE )
300- os .remove (PIDFILE )
274+ myPidFile .close ()
275+ os .remove (PIDFILE )
301276
302277 if restart :
303278 logger .info ("SpeakReader is restarting..." )
@@ -332,7 +307,7 @@ def main():
332307 sys .exit (0 )
333308
334309
335- def daemonize (CREATEPID , PIDFILE ):
310+ def daemonize (myPidFile ):
336311
337312 if threading .activeCount () != 1 :
338313 logger .warn (
@@ -377,12 +352,10 @@ def daemonize(CREATEPID, PIDFILE):
377352 os .dup2 (se .fileno (), sys .stderr .fileno ())
378353
379354 pid = os .getpid ()
380- logger .info (u"Daemonized to PID: %d" , pid )
381-
382- if CREATEPID :
383- logger .info (u"Writing PID %d to %s" , pid , PIDFILE )
384- with open (PIDFILE , 'w' ) as fp :
385- fp .write ("%s\n " % pid )
355+ myPidFile .seek (0 )
356+ myPidFile .write (str (pid ))
357+ myPidFile .flush ()
358+ logger .info ("Daemonized to PID: %d" , pid )
386359
387360
388361def check_folder_writable (folder , fallback , name ):
@@ -393,17 +366,17 @@ def check_folder_writable(folder, fallback, name):
393366 try :
394367 os .makedirs (folder )
395368 except OSError as e :
396- logger .error (u "Could not create %s dir '%s': %s" % (name , folder , e ))
369+ logger .error ("Could not create %s dir '%s': %s" % (name , folder , e ))
397370 if folder != fallback :
398- logger .warn (u "Falling back to %s dir '%s'" % (name , fallback ))
371+ logger .warn ("Falling back to %s dir '%s'" % (name , fallback ))
399372 return check_folder_writable (None , fallback , name )
400373 else :
401374 return folder , None
402375
403376 if not os .access (folder , os .W_OK ):
404- logger .error (u "Cannot write to %s dir '%s'" % (name , folder ))
377+ logger .error ("Cannot write to %s dir '%s'" % (name , folder ))
405378 if folder != fallback :
406- logger .warn (u "Falling back to %s dir '%s'" % (name , fallback ))
379+ logger .warn ("Falling back to %s dir '%s'" % (name , fallback ))
407380 return check_folder_writable (None , fallback , name )
408381 else :
409382 return folder , False
0 commit comments