Add pidfile cmdline option

Change-Id: Ibaa61b624eb6683b1be6901a7b29f5f73aad27b2
Signed-off-by: Pierre Pfister <ppfister@cisco.com>
diff --git a/src/vlib/unix/main.c b/src/vlib/unix/main.c
index f52316e..c90e133 100644
--- a/src/vlib/unix/main.c
+++ b/src/vlib/unix/main.c
@@ -317,6 +317,7 @@
   unix_main_t *um = &unix_main;
   clib_error_t *error = 0;
   gid_t gid;
+  int pidfd = -1;
 
   /* Defaults */
   um->cli_pager_buffer_limit = UNIX_CLI_DEFAULT_PAGER_LIMIT;
@@ -415,11 +416,38 @@
 	  if (setegid (gid) == -1)
 	    return clib_error_return_unix (0, "setegid");
 	}
+      else if (unformat (input, "pidfile %s", &um->pidfile))
+	;
       else
 	return clib_error_return (0, "unknown input `%U'",
 				  format_unformat_error, input);
     }
 
+  if (um->runtime_dir == 0)
+    {
+      uid_t uid = geteuid ();
+      if (uid == 00)
+	um->runtime_dir = format (0, "/run/%s%c",
+				  vlib_default_runtime_dir, 0);
+      else
+	um->runtime_dir = format (0, "/run/user/%u/%s%c", uid,
+				  vlib_default_runtime_dir, 0);
+    }
+
+  if (um->pidfile)
+    {
+      if ((error = vlib_unix_validate_runtime_file (um,
+						    (char *) um->pidfile,
+						    &um->pidfile)))
+	return error;
+
+      if (((pidfd = open ((char *) um->pidfile,
+			  O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0))
+	{
+	  return clib_error_return_unix (0, "open");
+	}
+    }
+
   error = setup_signal_handlers (um);
   if (error)
     return error;
@@ -434,19 +462,21 @@
 						       0) < 0)
 	clib_error_return (0, "daemon () fails");
     }
-  um->unix_config_complete = 1;
 
-  if (um->runtime_dir == 0)
+  if (pidfd >= 0)
     {
-      uid_t uid = geteuid ();
-      if (uid == 00)
-	um->runtime_dir = format (0, "/run/%s%c",
-				  vlib_default_runtime_dir, 0);
-      else
-	um->runtime_dir = format (0, "/run/user/%u/%s%c", uid,
-				  vlib_default_runtime_dir, 0);
+      u8 *lv = format (0, "%d", getpid ());
+      if (write (pidfd, (char *) lv, vec_len (lv)) != vec_len (lv))
+	{
+	  vec_free (lv);
+	  close (pidfd);
+	  return clib_error_return_unix (0, "write");
+	}
+      vec_free (lv);
+      close (pidfd);
     }
 
+  um->unix_config_complete = 1;
 
   return 0;
 }
@@ -475,6 +505,9 @@
  * Very useful in situations where folks don't remember or can't be bothered
  * to include CLI commands in bug reports.
  *
+ * @cfgcmd{pidfile, &lt;filename&gt;}
+ * Writes the pid of the main thread in @c filename.
+ *
  * @cfgcmd{full-coredump}
  * Ask the Linux kernel to dump all memory-mapped address regions, instead
  * of just text+data+bss.
diff --git a/src/vlib/unix/unix.h b/src/vlib/unix/unix.h
index ee1312e..97f5894 100644
--- a/src/vlib/unix/unix.h
+++ b/src/vlib/unix/unix.h
@@ -106,6 +106,9 @@
   /* runtime directory path */
   u8 *runtime_dir;
 
+  /* pidfile filename */
+  u8 *pidfile;
+
   /* unix config complete */
   volatile int unix_config_complete;
 
@@ -241,6 +244,10 @@
 
 clib_error_t *vlib_unix_recursive_mkdir (char *path);
 
+clib_error_t *vlib_unix_validate_runtime_file (unix_main_t * um,
+					       const char *path,
+					       u8 ** full_path);
+
 #endif /* included_unix_unix_h */
 
 /*
diff --git a/src/vlib/unix/util.c b/src/vlib/unix/util.c
index 93aeb99..312cc9b 100644
--- a/src/vlib/unix/util.c
+++ b/src/vlib/unix/util.c
@@ -257,6 +257,55 @@
   return error;
 }
 
+clib_error_t *
+vlib_unix_validate_runtime_file (unix_main_t * um,
+				 const char *path, u8 ** full_path)
+{
+  u8 *fp = 0;
+  char *last_slash = 0;
+
+  if (path[0] == '\0')
+    {
+      return clib_error_return (0, "path is an empty string");
+    }
+  else if (strncmp (path, "../", 3) == 0 || strstr (path, "/../"))
+    {
+      return clib_error_return (0, "'..' not allowed in runtime path");
+    }
+  else if (path[0] == '/')
+    {
+      /* Absolute path. Has to start with runtime directory */
+      if (strncmp ((char *) um->runtime_dir, path,
+		   strlen ((char *) um->runtime_dir)))
+	{
+	  return clib_error_return (0,
+				    "file %s is not in runtime directory %s",
+				    path, um->runtime_dir);
+	}
+      fp = format (0, "%s%c", path, '\0');
+    }
+  else
+    {
+      /* Relative path, just append to runtime */
+      fp = format (0, "%s/%s%c", um->runtime_dir, path, '\0');
+    }
+
+  /* We don't want to create a directory out of the last file */
+  if ((last_slash = strrchr ((char *) fp, '/')) != NULL)
+    *last_slash = '\0';
+
+  clib_error_t *error = vlib_unix_recursive_mkdir ((char *) fp);
+
+  if (last_slash != NULL)
+    *last_slash = '/';
+
+  if (error)
+    vec_free (fp);
+
+  *full_path = fp;
+  return error;
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *