struct pidfh * pidfile_open(const char *path, mode_t mode, pid_t *pidptr) { struct pidfh *pfh; struct stat sb; int error, fd, len, count; struct timespec rqtp; pfh = malloc(sizeof(*pfh)); if (pfh == NULL) return (NULL); if (path == NULL) len = snprintf(pfh->pf_path, sizeof(pfh->pf_path), "/var/run/%s.pid", getprogname()); else len = snprintf(pfh->pf_path, sizeof(pfh->pf_path), "%s", path); if (len >= (int)sizeof(pfh->pf_path)) { free(pfh); errno = ENAMETOOLONG; return (NULL); } /* * Open the PID file and obtain exclusive lock. * We truncate PID file here only to remove old PID immediatelly, * PID file will be truncated again in pidfile_write(), so * pidfile_write() can be called multiple times. */ fd = flopen(pfh->pf_path, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode); if (fd == -1) { count = 0; rqtp.tv_sec = 0; rqtp.tv_nsec = 5000000; if (errno == EWOULDBLOCK && pidptr != NULL) { again: errno = pidfile_read(pfh->pf_path, pidptr); if (errno == 0) errno = EEXIST; else if (errno == EAGAIN) { if (++count <= 3) { nanosleep(&rqtp, 0); goto again; } } } free(pfh); return (NULL); } /* * Remember file information, so in pidfile_write() we are sure we write * to the proper descriptor. */ if (fstat(fd, &sb) == -1) { error = errno; unlink(pfh->pf_path); close(fd); free(pfh); errno = error; return (NULL); } pfh->pf_fd = fd; pfh->pf_dev = sb.st_dev; pfh->pf_ino = sb.st_ino; return (pfh); }
static int flopen_retry(const char *filename) { int fd, try; for (try = 1; try <= 1024; try *= 2) { fd = flopen(filename, PEFS_SESSION_FILE_FLAGS, PEFS_SESSION_FILE_MODE); if (fd != -1) return (fd); else if (errno != EWOULDBLOCK) return (-1); // Exponential back-off up to 1 second usleep(try * 1000000 / 1024); } errno = ETIMEDOUT; return (-1); } static int pefs_session_count_incr(const char *user, bool incr) { struct stat sb; struct timespec tp_uptime, tp_now; ssize_t offset; int fd, total = 0; char filename[MAXPATHLEN], buf[16]; const char *errstr; snprintf(filename, sizeof(filename), "%s/%s", PEFS_SESSION_DIR, user); if (lstat(PEFS_SESSION_DIR, &sb) == -1) { if (errno != ENOENT) { pefs_warn("unable to access session directory %s: %s", PEFS_SESSION_DIR, strerror(errno)); return (-1); } if (mkdir(PEFS_SESSION_DIR, PEFS_SESSION_DIR_MODE) == -1) { pefs_warn("unable to create session directory %s: %s", PEFS_SESSION_DIR, strerror(errno)); return (-1); } } else if (!S_ISDIR(sb.st_mode)) { pefs_warn("%s is not a directory", PEFS_SESSION_DIR); return (-1); } if ((fd = flopen_retry(filename)) == -1) { pefs_warn("unable to create session counter file %s: %s", filename, strerror(errno)); return (-1); } if ((offset = pread(fd, buf, sizeof(buf) - 1, 0)) == -1) { pefs_warn("unable to read from the session counter file %s: %s", filename, strerror(errno)); close(fd); return (-1); } buf[offset] = '\0'; if (offset != 0) { total = strtonum(buf, 0, INT_MAX, &errstr); if (errstr != NULL) pefs_warn("corrupted session counter file: %s: %s", filename, errstr); } /* * Determine if this is the first increment of the session file. * * It is considered the first increment if the session file has not * been modified since the last boot time. */ if (incr && total > 0) { if (fstat(fd, &sb) == -1) { pefs_warn("unable to access session counter file %s: %s", filename, strerror(errno)); close(fd); return (-1); } /* * Check is messy and will fail if wall clock isn't monotonical * (e.g. because of ntp, DST, leap seconds) */ clock_gettime(CLOCK_REALTIME_FAST, &tp_now); clock_gettime(CLOCK_UPTIME_FAST, &tp_uptime); if (sb.st_mtime < tp_now.tv_sec - tp_uptime.tv_sec) { pefs_warn("stale session counter file: %s", filename); total = 0; } } lseek(fd, 0L, SEEK_SET); ftruncate(fd, 0L); total += incr ? 1 : -1; if (total < 0) { pefs_warn("corrupted session counter file: %s", filename); total = 0; } else pefs_warn("%s: session count %d", user, total); buf[0] = '\0'; snprintf(buf, sizeof(buf), "%d", total); pwrite(fd, buf, strlen(buf), 0); close(fd); return (total); } static int pam_pefs_checkfs(const char *homedir) { char fsroot[MAXPATHLEN]; int error; error = pefs_getfsroot(homedir, 0, fsroot, sizeof(fsroot)); if (error != 0) { pefs_warn("file system is not mounted: %s", homedir); return (PAM_USER_UNKNOWN); } if (strcmp(fsroot, homedir) != 0) { pefs_warn("file system is not mounted on home dir: %s", fsroot); return (PAM_USER_UNKNOWN); } return (PAM_SUCCESS); }