Skip to content

Commit

Permalink
Add lockfile to prevent multiple instances of the script from running…
Browse files Browse the repository at this point in the history
… simultaneously (#1)
  • Loading branch information
nicholasodonnell authored Jan 8, 2024
1 parent 5f558b4 commit cfc266a
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ __pycache__/
smart-runner.conf
smart-runner.json
smart-runner.log
.smart-runner.lock
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ S.M.A.R.T Runner is a Python script designed to automate the scheduling and exec
- **Parallel Testing:** Specify how many disks to test simultaneously for both short and long tests.
- **Offset Timing:** Configure waiting periods between tests to avoid overloading the system or testing too frequently.
- **Logging:** Keep track of tests, results, and any errors in a specified log file.
- **Email Notifications:** Receive email alerts for test failures or potential disk issues.
- **Email Notifications:** Receive email alerts for test failures.
- **Configurable SMTP:** Set up custom SMTP server settings for sending out email notifications.
- **Easy Cron Integration:** Run the script as a daily cron job for hands-off operation.
- **Intelligent Disk Detection:** Automatically detect and handle disks based on mount points if specified.
- **Lock File:** Prevent multiple instances of the script from running simultaneously.

## Setup Requirements

Expand All @@ -39,7 +40,7 @@ The script is configured via a `smart-runner.conf` file. Here's an explanation o
| Section | Option | Description |
| ---------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `disks` | (list) | List the devices or mounts to test. Devices are listed as `/dev/sdX`. If a mount is given the disk will automatically be resolved. |
| `database` | `file` | Path to the file where test schedules and results are stored. |
| `database` | `file` | Path to the file where test dates are stored. |
| `short` | `enabled` | Set to `true` to enable short SMART tests. |
| | `frequency_days` | How often (in days) to run a short test on each disk. |
| | `disks_per_run` | Number of disks to test in parallel for short tests. Set to 0 for all. |
Expand Down
4 changes: 2 additions & 2 deletions smart-runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Database,
Disk,
Email,
Lock,
Logger,
Message,
NotEnabledException,
Expand All @@ -20,7 +21,6 @@
TooSoonException,
)
from sys import exit
from traceback import print_exc


def sendTestFailureEmail(config, logger, test, disk):
Expand Down Expand Up @@ -55,6 +55,7 @@ def sendTestFailureEmail(config, logger, test, disk):

def main():
try:
Lock()
args = Args()
config = Config(configFile=args.config)
db = Database(dbFile=config.database.file)
Expand Down Expand Up @@ -175,7 +176,6 @@ def main():

except Exception as e:
print("Error: {}".format(e))
print_exc(e)
exit(1)


Expand Down
1 change: 1 addition & 0 deletions smart_runner/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
from .database import *
from .disk import *
from .email import *
from .lock import *
from .logger import *
from .test import *
32 changes: 32 additions & 0 deletions smart_runner/lock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from atexit import register
from os import getcwd, remove

LOCK_FILE_PATH = getcwd() + "/.smart-runner.lock"


class Lock:
def __init__(self):
if self.isLocked():
raise RuntimeError("smart-runner is already running")

self.createLock()

# Register the removeLock function to be called on exit
register(self.removeLock)

def createLock(self):
with open(LOCK_FILE_PATH, "w") as lockFile:
lockFile.write("")

def removeLock(self):
try:
remove(LOCK_FILE_PATH)
except FileNotFoundError:
pass # If the file is already removed or doesn't exist, ignore the error

def isLocked(self):
try:
with open(LOCK_FILE_PATH, "r") as lockFile:
return True
except FileNotFoundError:
return False

0 comments on commit cfc266a

Please sign in to comment.