Merge branch 'devel' of ssh://git.planet-lab.org/git/nodemanager into devel
[nodemanager.git] / tools.py
index 80685de..99e6dac 100644 (file)
--- a/tools.py
+++ b/tools.py
@@ -3,7 +3,7 @@
 
 """A few things that didn't seem to fit anywhere else."""
 
-import os
+import os, os.path
 import pwd
 import tempfile
 import fcntl
@@ -13,7 +13,7 @@ import subprocess
 
 import logger
 
-PID_FILE = '/var/run/nm.pid'
+PID_FILE = '/var/run/nodemanager.pid'
 
 ####################
 def get_default_if():
@@ -60,11 +60,11 @@ def daemon():
     os.setsid()
     if os.fork() != 0: os._exit(0)
     os.chdir('/')
-    os.umask(0)
+    os.umask(0022)
     devnull = os.open(os.devnull, os.O_RDWR)
     os.dup2(devnull, 0)
     # xxx fixme - this is just to make sure that nothing gets stupidly lost - should use devnull
-    crashlog = os.open('/var/log/nm.daemon', os.O_RDWR | os.O_APPEND | os.O_CREAT, 0644)
+    crashlog = os.open('/var/log/nodemanager.daemon', os.O_RDWR | os.O_APPEND | os.O_CREAT, 0644)
     os.dup2(crashlog, 1)
     os.dup2(crashlog, 2)
 
@@ -91,7 +91,9 @@ def fork_as(su, function, *args):
 ####################
 # manage files
 def pid_file():
-    """We use a pid file to ensure that only one copy of NM is running at a given time.  If successful, this function will write a pid file containing the pid of the current process.  The return value is the pid of the other running process, or None otherwise."""
+    """We use a pid file to ensure that only one copy of NM is running at a given time.
+If successful, this function will write a pid file containing the pid of the current process.
+The return value is the pid of the other running process, or None otherwise."""
     other_pid = None
     if os.access(PID_FILE, os.F_OK):  # check for a pid file
         handle = open(PID_FILE)  # pid file exists, read it
@@ -121,25 +123,35 @@ def write_temp_file(do_write, mode=None, uidgid=None):
     return temporary_filename
 
 # replace a target file with a new contents - checks for changes
-# return True if a change occurred, in which case
-# chown/chmod settings should be taken care of
-def replace_file_with_string (target, new_contents):
+# can handle chmod if requested
+# can also remove resulting file if contents are void, if requested
+# performs atomically:
+#    writes in a tmp file, which is then renamed (from sliverauth originally)
+# returns True if a change occurred, or the file is deleted
+def replace_file_with_string (target, new_contents, chmod=None, remove_if_empty=False):
     try:
         current=file(target).read()
     except:
         current=""
-    # xxx if verbose, report diffs...
     if current==new_contents:
+        # if turns out to be an empty string, and remove_if_empty is set,
+        # then make sure to trash the file if it exists
+        if remove_if_empty and not new_contents and os.path.isfile(target):
+            logger.verbose("tools.replace_file_with_string: removing file %s"%target)
+            try: os.unlink(target)
+            finally: return True
         return False
-    # overwrite target file
-    f=file(target,'w')
-    f.write(new_contents)
-    f.close()
+    # overwrite target file: create a temp in the same directory
+    path=os.path.dirname(target) or '.'
+    fd, name = tempfile.mkstemp('','repl',path)
+    os.write(fd,new_contents)
+    os.close(fd)
+    if os.path.exists(target):
+        os.unlink(target)
+    os.rename(name,target)
+    if chmod: os.chmod(target,chmod)
     return True
 
-# not needed yet - should that unlink the new file ?
-#def replace_file_with_file (target, new):
-#    return replace_file_with_string (target, file(new).read())
 
 ####################
 # utilities functions to get (cached) information from the node