vkernel - enhance the pidfile option and fix memimg file scanning
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 8 Jul 2011 23:05:42 +0000 (16:05 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 8 Jul 2011 23:05:42 +0000 (16:05 -0700)
* Enhance the pidfile option to leave the file descriptor open and hold
  an active lock on the pidfile while the vkernel is running.

  This allows scripts to test whether the pidfile is real or stale.

* When scanning for memimg files only stop if we are unable to create
  a new file.  Skip files owned by other users instead of giving up.

* Adjust the vkernel manual page to document the pidfile feature.  Also
  document that -I can take a vknetd socket path.

share/man/man7/vkernel.7
sys/platform/vkernel/platform/init.c
sys/platform/vkernel64/platform/init.c

index 8ad53ff..1d0e6ba 100644 (file)
@@ -54,7 +54,7 @@
 .Op Fl l Ar cpulock
 .Op Fl m Ar size
 .Op Fl n Ar numcpus
-.Op Fl p Ar file
+.Op Fl p Ar pidfile
 .Op Fl r Ar file
 .Sh DESCRIPTION
 The
@@ -110,10 +110,13 @@ The
 .Ar interface
 argument is the name of a
 .Xr tap 4
-device node.
+device node or the path to a
+.Xr vknetd 8
+socket.
 The
 .Pa /dev/
-path prefix does not have to be specified and will be automatically prepended.
+path prefix does not have to be specified and will be automatically prepended
+for a device node.
 Specifying
 .Cm auto
 will pick the first unused
@@ -145,6 +148,12 @@ address is not assigned until the interface is brought up in the guest.
 The
 .Ar netmask
 argument applies to all interfaces for which an address is specified.
+.Pp
+When running multiple vkernels it is often more convenient to simply
+connect to a
+.Xr vknetd 8
+socket and let vknetd deal with the tap and/or bridge.  An example of
+this would be '/var/run/vknet:0.0.0.0:10.2.0.2/16'.
 .It Fl l Ar cpulock
 Specify which, if any, real CPUs to lock virtual CPUs to.
 .Ar cpulock
@@ -186,9 +195,21 @@ Up to 16 CPUs are supported.
 The virtual kernel must be built with
 .Cd options SMP
 to use this option and will default to 2 CPUs unless otherwise specified.
-.It Fl p Ar file
-Specify a file in which to store the process ID.
-A warning is issued if this file cannot be opened for writing.
+.It Fl p Ar pidfile
+Specify a pidfile in which to store the process ID.
+Scripts can use this file to locate the vkernel pid for the purpose of
+shutting down or killing it.
+.Pp
+The vkernel will hold a lock on the pidfile while running.
+Scripts may test for the lock to determine if the pidfile is valid or
+stale so as to avoid accidently killing a random process.
+Something like '/usr/bin/lockf -ks -t 0 pidfile echo -n' may be used
+to test the lock.
+A non-zero exit code indicates that the pidfile represents a running
+vkernel.
+.Pp
+An error is issued and the vkernel exits if this file cannot be opened for
+writing or if it is already locked by an active vkernel process.
 .It Fl r Ar file
 Specify a R/W disk image
 .Ar file
index 3f46fc8..dc9fb9e 100644 (file)
@@ -380,7 +380,6 @@ init_sys_memory(char *imageFile)
         * Figure out the system memory image size.  If an image file was
         * specified and -m was not specified, use the image file's size.
         */
-
        if (imageFile && stat(imageFile, &st) == 0 && Maxmem_bytes == 0)
                Maxmem_bytes = (vm_paddr_t)st.st_size;
        if ((imageFile == NULL || stat(imageFile, &st) < 0) && 
@@ -405,13 +404,18 @@ init_sys_memory(char *imageFile)
         * Generate an image file name if necessary, then open/create the
         * file exclusively locked.  Do not allow multiple virtual kernels
         * to use the same image file.
+        *
+        * Don't iterate through a million files if we do not have write
+        * access to the directory, stop if our open() failed on a
+        * non-existant file.  Otherwise opens can fail for any number
+        * of reasons (lock failed, file not owned or writable by us, etc).
         */
        if (imageFile == NULL) {
                for (i = 0; i < 1000000; ++i) {
                        asprintf(&imageFile, "/var/vkernel/memimg.%06d", i);
                        fd = open(imageFile, 
                                  O_RDWR|O_CREAT|O_EXLOCK|O_NONBLOCK, 0644);
-                       if (fd < 0 && errno == EWOULDBLOCK) {
+                       if (fd < 0 && stat(imageFile, &st) == 0) {
                                free(imageFile);
                                continue;
                        }
@@ -1221,24 +1225,34 @@ init_netif(char *netifExp[], int netifExpNum)
        close(s);
 }
 
+/*
+ * Create the pid file and leave it open and locked while the vkernel is
+ * running.  This allows a script to use /usr/bin/lockf to probe whether
+ * a vkernel is still running (so as not to accidently kill an unrelated
+ * process from a stale pid file).
+ */
 static
 void
-writepid( void )
+writepid(void)
 {
-       pid_t self;
-       FILE *fp;
+       char buf[32];
+       int fd;
 
        if (pid_file != NULL) {
-               self = getpid();
-               fp = fopen(pid_file, "w");
-
-               if (fp != NULL) {
-                       fprintf(fp, "%ld\n", (long)self);
-                       fclose(fp);
-               }
-               else {
-                       perror("Warning: couldn't open pidfile");
+               snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
+               fd = open(pid_file, O_RDWR|O_CREAT|O_EXLOCK|O_NONBLOCK, 0666);
+               if (fd < 0) {
+                       if (errno == EWOULDBLOCK) {
+                               perror("Failed to lock pidfile, "
+                                      "vkernel already running");
+                       } else {
+                               perror("Failed to create pidfile");
+                       }
+                       exit(EX_SOFTWARE);
                }
+               ftruncate(fd, 0);
+               write(fd, buf, strlen(buf));
+               /* leave the file open to maintain the lock */
        }
 }
 
@@ -1247,7 +1261,7 @@ void
 cleanpid( void ) 
 {
        if (pid_file != NULL) {
-               if ( unlink(pid_file) != 0 )
+               if (unlink(pid_file) < 0)
                        perror("Warning: couldn't remove pidfile");
        }
 }
index ad6af17..33ed964 100644 (file)
@@ -379,7 +379,6 @@ init_sys_memory(char *imageFile)
         * Figure out the system memory image size.  If an image file was
         * specified and -m was not specified, use the image file's size.
         */
-
        if (imageFile && stat(imageFile, &st) == 0 && Maxmem_bytes == 0)
                Maxmem_bytes = (vm_paddr_t)st.st_size;
        if ((imageFile == NULL || stat(imageFile, &st) < 0) &&
@@ -404,13 +403,17 @@ init_sys_memory(char *imageFile)
         * Generate an image file name if necessary, then open/create the
         * file exclusively locked.  Do not allow multiple virtual kernels
         * to use the same image file.
+        *
+        * Don't iterate through a million files if we do not have write
+        * access to the directory, stop if our open() failed on a
+        * non-existant file.  Otherwise opens can fail for any number
         */
        if (imageFile == NULL) {
                for (i = 0; i < 1000000; ++i) {
                        asprintf(&imageFile, "/var/vkernel/memimg.%06d", i);
                        fd = open(imageFile,
                                  O_RDWR|O_CREAT|O_EXLOCK|O_NONBLOCK, 0644);
-                       if (fd < 0 && errno == EWOULDBLOCK) {
+                       if (fd < 0 && stat(imageFile, &st) == 0) {
                                free(imageFile);
                                continue;
                        }
@@ -1185,24 +1188,34 @@ init_netif(char *netifExp[], int netifExpNum)
        close(s);
 }
 
+/*
+ * Create the pid file and leave it open and locked while the vkernel is
+ * running.  This allows a script to use /usr/bin/lockf to probe whether
+ * a vkernel is still running (so as not to accidently kill an unrelated
+ * process from a stale pid file).
+ */
 static
 void
-writepid( void )
+writepid(void)
 {
-       pid_t self;
-       FILE *fp;
+       char buf[32];
+       int fd;
 
        if (pid_file != NULL) {
-               self = getpid();
-               fp = fopen(pid_file, "w");
-
-               if (fp != NULL) {
-                       fprintf(fp, "%ld\n", (long)self);
-                       fclose(fp);
-               }
-               else {
-                       perror("Warning: couldn't open pidfile");
+               snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
+               fd = open(pid_file, O_RDWR|O_CREAT|O_EXLOCK|O_NONBLOCK, 0666);
+               if (fd < 0) {
+                       if (errno == EWOULDBLOCK) {
+                               perror("Failed to lock pidfile, "
+                                      "vkernel already running");
+                       } else {
+                               perror("Failed to create pidfile");
+                       }
+                       exit(EX_SOFTWARE);
                }
+               ftruncate(fd, 0);
+               write(fd, buf, strlen(buf));
+               /* leave the file open to maintain the lock */
        }
 }
 
@@ -1211,7 +1224,7 @@ void
 cleanpid( void )
 {
        if (pid_file != NULL) {
-               if ( unlink(pid_file) != 0 )
+               if (unlink(pid_file) < 0)
                        perror("Warning: couldn't remove pidfile");
        }
 }