summaryrefslogtreecommitdiffstats
path: root/lib/clplumbing/cl_pidfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/clplumbing/cl_pidfile.c')
-rw-r--r--lib/clplumbing/cl_pidfile.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/lib/clplumbing/cl_pidfile.c b/lib/clplumbing/cl_pidfile.c
new file mode 100644
index 0000000..b94573b
--- /dev/null
+++ b/lib/clplumbing/cl_pidfile.c
@@ -0,0 +1,294 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lha_internal.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <string.h>
+#include <clplumbing/cl_signal.h>
+#include <clplumbing/cl_pidfile.h>
+#include <clplumbing/lsb_exitcodes.h>
+
+/*
+ * The following information is from the Filesystem Hierarchy Standard
+ * version 2.1 dated 12 April, 2000.
+ *
+ * 5.6 /var/lock : Lock files
+ * Lock files should be stored within the /var/lock directory structure.
+ * Device lock files, such as the serial device lock files that were originally
+ * found in either /usr/spool/locks or /usr/spool/uucp, must now be stored in
+ * /var/lock. The naming convention which must be used is LCK.. followed by
+ * the base name of the device file. For example, to lock /dev/cua0 the file
+ * LCK..cua0 would be created.
+ *
+ * The format used for device lock files must be the HDB UUCP lock file format.
+ * The HDB format is to store the process identifier (PID) as a ten byte
+ * ASCII decimal number, with a trailing newline. For example, if process 1230
+ * holds a lock file, it would contain the eleven characters: space, space,
+ * space, space, space, space, one, two, three, zero, and newline.
+ * Then, anything wishing to use /dev/cua0 can read the lock file and act
+ * accordingly (all locks in /var/lock should be world-readable).
+ *
+ *
+ * PERMISSIONS NOTE:
+ * Different linux distributions set the mode of the lock directory differently
+ * Any process which wants to create lock files must have write permissions
+ * on FILE_LOCK_D (probably /var/lock). For things like the heartbeat API
+ * code, this may mean allowing the uid of the processes that use this API
+ * to join group uucp, or making the binaries setgid to uucp.
+ */
+
+/* The code in this file originally written by Guenther Thomsen */
+/* Somewhat mangled by Alan Robertson */
+
+/*
+ * Lock a tty (using lock files, see linux `man 2 open` close to O_EXCL)
+ * serial_device has to be _the complete path_, i.e. including '/dev/' to the
+ * special file, which denotes the tty to lock -tho
+ * return 0 on success,
+ * -1 if device is locked (lockfile exists and isn't stale),
+ * -2 for temporarily failure, try again,
+ * other negative value, if something unexpected happend (failure anyway)
+ */
+
+
+/* This is what the FHS standard specifies for the size of our lock file */
+#define LOCKSTRLEN 11
+#include <clplumbing/cl_log.h>
+static int IsRunning(long pid)
+{
+ int rc = 0;
+ long mypid;
+ int running = 0;
+ char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
+
+ /* check if pid is running */
+ if (CL_KILL(pid, 0) < 0 && errno == ESRCH) {
+ goto bail;
+ }
+
+#ifndef HAVE_PROC_PID
+ return 1;
+#endif
+
+ /* check to make sure pid hasn't been reused by another process */
+ snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid);
+
+ rc = readlink(proc_path, exe_path, PATH_MAX-1);
+ if(rc < 0) {
+ cl_perror("Could not read from %s", proc_path);
+ goto bail;
+ }
+ exe_path[rc] = 0;
+
+ mypid = (unsigned long) getpid();
+
+ snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", mypid);
+ rc = readlink(proc_path, myexe_path, PATH_MAX-1);
+ if(rc < 0) {
+ cl_perror("Could not read from %s", proc_path);
+ goto bail;
+ }
+ myexe_path[rc] = 0;
+
+ if(strcmp(exe_path, myexe_path) == 0) {
+ running = 1;
+ }
+
+ bail:
+ return running;
+}
+
+static int
+DoLock(const char *filename)
+{
+ char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1];
+ int fd;
+ long pid, mypid;
+ int rc;
+ struct stat sbuf;
+
+ mypid = (unsigned long) getpid();
+
+ snprintf(lf_name, sizeof(lf_name), "%s",filename);
+
+ snprintf(tf_name, sizeof(tf_name), "%s.%lu",
+ filename, mypid);
+
+ if ((fd = open(lf_name, O_RDONLY)) >= 0) {
+ if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
+ sleep(1); /* if someone was about to create one,
+ * give'm a sec to do so
+ * Though if they follow our protocol,
+ * this won't happen. They should really
+ * put the pid in, then link, not the
+ * other way around.
+ */
+ }
+ if (read(fd, buf, sizeof(buf)) < 1) {
+ /* lockfile empty -> rm it and go on */;
+ } else {
+ if (sscanf(buf, "%lu", &pid) < 1) {
+ /* lockfile screwed up -> rm it and go on */
+ } else {
+ if (pid > 1 && (getpid() != pid)
+ && IsRunning(pid)) {
+ /* is locked by existing process
+ * -> give up */
+ close(fd);
+ return -1;
+ } else {
+ /* stale lockfile -> rm it and go on */
+ }
+ }
+ }
+ unlink(lf_name);
+ close(fd);
+ }
+ if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
+ /* Hmmh, why did we fail? Anyway, nothing we can do about it */
+ return -3;
+ }
+
+ /* Slight overkill with the %*d format ;-) */
+ snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid);
+
+ if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
+ /* Again, nothing we can do about this */
+ rc = -3;
+ close(fd);
+ goto out;
+ }
+ close(fd);
+
+ switch (link(tf_name, lf_name)) {
+ case 0:
+ if (stat(tf_name, &sbuf) < 0) {
+ /* something weird happened */
+ rc = -3;
+ break;
+ }
+ if (sbuf.st_nlink < 2) {
+ /* somehow, it didn't get through - NFS trouble? */
+ rc = -2;
+ break;
+ }
+ rc = 0;
+ break;
+ case EEXIST:
+ rc = -1;
+ break;
+ default:
+ rc = -3;
+ }
+ out:
+ unlink(tf_name);
+ return rc;
+}
+
+static int
+DoUnlock(const char * filename)
+{
+ char lf_name[256];
+
+ snprintf(lf_name, sizeof(lf_name), "%s", filename);
+
+ return unlink(lf_name);
+}
+
+
+int
+cl_read_pidfile(const char*filename)
+{
+ long pid = 0;
+
+ pid = cl_read_pidfile_no_checking(filename);
+
+ if (pid < 0){
+ return - LSB_STATUS_STOPPED;
+ }
+
+ if (IsRunning(pid)){
+ return pid;
+ }else{
+ return -LSB_STATUS_VAR_PID;
+ }
+}
+
+
+int
+cl_read_pidfile_no_checking(const char*filename)
+{
+ int fd;
+ long pid = 0;
+ char buf[LOCKSTRLEN+1];
+ if ((fd = open(filename, O_RDONLY)) < 0) {
+ return -1;
+ }
+
+ if (read(fd, buf, sizeof(buf)) < 1) {
+ close(fd);
+ return -1;
+ }
+
+ if (sscanf(buf, "%lu", &pid) <= 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (pid <= 0){
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return pid;
+}
+
+
+int
+cl_lock_pidfile(const char *filename)
+{
+ if (filename == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+ return DoLock(filename);
+}
+
+/*
+ * Unlock a file (remove its lockfile)
+ * do we need to check, if its (still) ours? No, IMHO, if someone else
+ * locked our line, it's his fault -tho
+ * returns 0 on success
+ * <0 if some failure occured
+ */
+
+int
+cl_unlock_pidfile(const char *filename)
+{
+ if (filename == NULL) {
+ errno = EFAULT;
+ return -3;
+ }
+
+ return(DoUnlock(filename));
+}