/* return the pid in a pidfile. return 0 if the process (or pidfile) does not exist */ pid_t pidfile_pid(const char *name) { int fd; char pidstr[20]; pid_t pid; unsigned int ret; pstring pidFile; slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_piddir(), name); fd = sys_open(pidFile, O_NONBLOCK | O_RDONLY, 0644); if (fd == -1) { return 0; } ZERO_ARRAY(pidstr); if (read(fd, pidstr, sizeof(pidstr)-1) <= 0) { goto noproc; } ret = atoi(pidstr); if (ret == 0) { /* Obviously we had some garbage in the pidfile... */ DEBUG(1, ("Could not parse contents of pidfile %s\n", pidFile)); goto noproc; } pid = (pid_t)ret; if (!process_exists_by_pid(pid)) { goto noproc; } if (fcntl_lock(fd,SMB_F_SETLK,0,1,F_RDLCK)) { /* we could get the lock - it can't be a Samba process */ goto noproc; } close(fd); return (pid_t)ret; noproc: close(fd); unlink(pidFile); return 0; }
/***************************************************** lock the shared variable area *******************************************************/ static void lockit(void) { if (shared_fd == 0) { char *p = getenv("SMBW_HANDLE"); if (!p) { DEBUG(0,("ERROR: can't get smbw shared handle\n")); exit(1); } shared_fd = atoi(p); } if (locked==0 && fcntl_lock(shared_fd,SMB_F_SETLKW,0,1,F_WRLCK)==False) { DEBUG(0,("ERROR: can't get smbw shared lock (%s)\n", strerror(errno))); exit(1); } locked++; }
/** * return the pid in a pidfile. return 0 if the process (or pidfile) * does not exist */ pid_t pidfile_pid(const char *piddir, const char *name) { int fd; char pidstr[20]; pid_t ret; char *pidFile; if (asprintf(&pidFile, "%s/%s.pid", piddir, name) < 0) { return 0; } fd = open(pidFile, O_NONBLOCK | O_RDONLY, 0644); if (fd == -1) { SAFE_FREE(pidFile); return 0; } ZERO_STRUCT(pidstr); if (read(fd, pidstr, sizeof(pidstr)-1) <= 0) { goto noproc; } ret = (pid_t)atoi(pidstr); if (!process_exists_by_pid(ret)) { goto noproc; } if (fcntl_lock(fd,F_SETLK,0,1,F_RDLCK)) { /* we could get the lock - it can't be a Samba process */ goto noproc; } close(fd); SAFE_FREE(pidFile); return ret; noproc: close(fd); unlink(pidFile); SAFE_FREE(pidFile); return 0; }
/******************************************************************* unlock a share mode file. ******************************************************************/ static BOOL slow_unlock_share_entry(int cnum, uint32 dev, uint32 inode, int token) { int fd = (int)token; int ret = True; struct stat sb; pstring fname; if (read_only) return True; /* Fix for zero length share files from Gerald Werner <*****@*****.**> */ share_name(cnum, dev, inode, fname); /* get the share mode file size */ if(fstat((int)token, &sb) != 0) { DEBUG(0,("ERROR: unlock_share_entry: Failed to do stat on share file %s (%s)\n", fname, strerror(errno))); sb.st_size = 1; ret = False; } /* If the file was zero length, we must delete before doing the unlock to avoid a race condition (see the code in lock_share_mode_entry for details. */ /* remove the share file if zero length */ if(sb.st_size == 0) delete_share_file(cnum, fname); /* token is the fd of the open share mode file. */ /* Unlock the first byte. */ if(fcntl_lock(fd, F_SETLKW, 0, 1, F_UNLCK) == False) { DEBUG(0,("ERROR unlock_share_entry: fcntl_lock failed with %s\n", strerror(errno))); ret = False; } close(fd); return ret; }
/* a byte range locking function - return 0 on success this functions locks/unlocks "len" byte at the specified offset. On error, errno is also set so that errors are passed back properly through tdb_open(). note that a len of zero means lock to end of file */ int tdb_brlock(struct tdb_context *tdb, int rw_type, tdb_off_t offset, size_t len, enum tdb_lock_flags flags) { int ret; if (tdb->flags & TDB_NOLOCK) { return 0; } if (flags & TDB_LOCK_MARK_ONLY) { return 0; } if ((rw_type == F_WRLCK) && (tdb->read_only || tdb->traverse_read)) { tdb->ecode = TDB_ERR_RDONLY; return -1; } do { ret = fcntl_lock(tdb, rw_type, offset, len, flags & TDB_LOCK_WAIT); /* Check for a sigalarm break. */ if (ret == -1 && errno == EINTR && tdb->interrupt_sig_ptr && *tdb->interrupt_sig_ptr) { break; } } while (ret == -1 && errno == EINTR); if (ret == -1) { tdb->ecode = TDB_ERR_LOCK; /* Generic lock error. errno set by fcntl. * EAGAIN is an expected return from non-blocking * locks. */ if (!(flags & TDB_LOCK_PROBE) && errno != EAGAIN) { TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %u rw_type=%d flags=%d len=%zu\n", tdb->fd, offset, rw_type, flags, len)); } return -1; } return 0; }
/** * create a pid file in the pid directory. open it and leave it locked */ void pidfile_create(const char *piddir, const char *name) { int fd; char buf[20]; char *pidFile; pid_t pid; if (asprintf(&pidFile, "%s/%s.pid", piddir, name) < 0) { DEBUG(0,("ERROR: Out of memory\n")); exit(1); } pid = pidfile_pid(piddir, name); if (pid != 0) { DEBUG(0,("ERROR: %s is already running. File %s exists and process id %d is running.\n", name, pidFile, (int)pid)); exit(1); } fd = open(pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_EXCL, 0644); if (fd == -1) { DEBUG(0,("ERROR: can't open %s: Error was %s\n", pidFile, strerror(errno))); exit(1); } if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==false) { DEBUG(0,("ERROR: %s : fcntl lock of file %s failed. Error was %s\n", name, pidFile, strerror(errno))); exit(1); } memset(buf, 0, sizeof(buf)); slprintf(buf, sizeof(buf) - 1, "%u\n", (unsigned int) getpid()); if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) { DEBUG(0,("ERROR: can't write to file %s: %s\n", pidFile, strerror(errno))); exit(1); } /* Leave pid file open & locked for the duration... */ SAFE_FREE(pidFile); }
/******************************************************************* lock a share mode file. ******************************************************************/ static BOOL slow_lock_share_entry(int cnum, uint32 dev, uint32 inode, int *ptok) { pstring fname; int fd; int ret = True; *ptok = (int)-1; if(!share_name(cnum, dev, inode, fname)) return False; if (read_only) return True; /* we need to do this as root */ become_root(False); { BOOL gotlock = False; /* * There was a race condition in the original slow share mode code. * A smbd could open a share mode file, and before getting * the lock, another smbd could delete the last entry for * the share mode file and delete the file entry from the * directory. Thus this smbd would be left with a locked * share mode fd attached to a file that no longer had a * directory entry. Thus another smbd would think that * there were no outstanding opens on the file. To fix * this we now check we can do a stat() call on the filename * before allowing the lock to proceed, and back out completely * and try the open again if we cannot. * Jeremy Allison ([email protected]). */ do { struct stat dummy_stat; fd = (int)open(fname,read_only?O_RDONLY:(O_RDWR|O_CREAT), SHARE_FILE_MODE); if(fd < 0) { DEBUG(0,("ERROR lock_share_entry: failed to open share file %s. Error was %s\n", fname, strerror(errno))); ret = False; break; } /* At this point we have an open fd to the share mode file. Lock the first byte exclusively to signify a lock. */ if(fcntl_lock(fd, F_SETLKW, 0, 1, F_WRLCK) == False) { DEBUG(0,("ERROR lock_share_entry: fcntl_lock on file %s failed with %s\n", fname, strerror(errno))); close(fd); ret = False; break; } /* * If we cannot stat the filename, the file was deleted between * the open and the lock call. Back out and try again. */ if(stat(fname, &dummy_stat)!=0) { DEBUG(2,("lock_share_entry: Re-issuing open on %s to fix race. Error was %s\n", fname, strerror(errno))); close(fd); } else gotlock = True; } while(!gotlock); /* * We have to come here if any of the above calls fail * as we don't want to return and leave ourselves running * as root ! */ } *ptok = (int)fd; /* return to our previous privilage level */ unbecome_root(False); return ret; }
/* create a pid file in the pid directory. open it and leave it locked */ void pidfile_create(const char *program_name) { int fd; char buf[20]; const char *short_configfile; char *name; char *pidFile; pid_t pid; /* Add a suffix to the program name if this is a process with a * none default configuration file name. */ if (is_default_dyn_CONFIGFILE()) { name = SMB_STRDUP(program_name); } else { short_configfile = strrchr( get_dyn_CONFIGFILE(), '/'); if (short_configfile == NULL) { /* conf file in current directory */ short_configfile = get_dyn_CONFIGFILE(); } else { /* full/relative path provided */ short_configfile++; } if (asprintf(&name, "%s-%s", program_name, short_configfile) == -1) { smb_panic("asprintf failed"); } } if (asprintf(&pidFile, "%s/%s.pid", lp_piddir(), name) == -1) { smb_panic("asprintf failed"); } pid = pidfile_pid(name); if (pid != 0) { DEBUG(0,("ERROR: %s is already running. File %s exists and process id %d is running.\n", name, pidFile, (int)pid)); exit(1); } fd = sys_open(pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_EXCL, 0644); if (fd == -1) { DEBUG(0,("ERROR: can't open %s: Error was %s\n", pidFile, strerror(errno))); exit(1); } if (fcntl_lock(fd,SMB_F_SETLK,0,1,F_WRLCK)==False) { DEBUG(0,("ERROR: %s : fcntl lock of file %s failed. Error was %s\n", name, pidFile, strerror(errno))); exit(1); } memset(buf, 0, sizeof(buf)); slprintf(buf, sizeof(buf) - 1, "%u\n", (unsigned int) sys_getpid()); if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) { DEBUG(0,("ERROR: can't write to file %s: %s\n", pidFile, strerror(errno))); exit(1); } /* Leave pid file open & locked for the duration... */ SAFE_FREE(name); SAFE_FREE(pidFile); }
int main(int ac, char **av) { const char * const fifoname = "./fio.fifo"; const char * const filename = "./fio.file"; const mode_t mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; FILE *file; char line[BUFSIZ]; const int lock = 1; int errors = 0; int fd, wfd; if (ac == 2 && !strcmp(av[1], "help")) { printf("usage: %s\n", *av); return EXIT_SUCCESS; } printf("Testing: %s\n", "fio"); umask(0); if ((fd = fifo_open(fifoname, mode, lock, &wfd)) == -1) { ++errors, printf("Test1: fifo_open(\"%s\", %d, %d) failed (%s)\n", fifoname, (int)mode, lock, strerror(errno)); #ifndef HAVE_FCNTL_THAT_CAN_LOCK_FIFOS printf("\n Can your system lock fifos?\n\n"); #endif } else { if ((fcntl_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0)) != -1) ++errors, printf("Test2: fcntl_lock(wrlock) failed\n"); /* Should really test that the following non-blocking changes do occur */ if (nonblock_on(fd) == -1) ++errors, printf("Test3: nonblock_on() failed (%s)\n", strerror(errno)); if (nonblock_off(fd) == -1) ++errors, printf("Test4: nonblock_off() failed (%s)\n", strerror(errno)); if (fcntl_set_flag(fd, O_NONBLOCK) == -1) ++errors, printf("Test5: fcntl_set_flag() failed (%s)\n", strerror(errno)); if (fcntl_clear_flag(fd, O_NONBLOCK) == -1) ++errors, printf("Test6: fcntl_clear_flag() failed (%s)\n", strerror(errno)); close(fd); close(wfd); unlink(fifoname); } #define CHECK_FGETLINE(i, size, expected) \ if ((expected) && !fgetline(line, (size), file)) \ ++errors, printf("Test%d: fgetline() failed\n", (i)); \ else if ((expected) && strcmp(line, ((expected) ? (expected) : ""))) \ ++errors, printf("Test%d: fgetline() read \"%s\", not \"%s\"\n", (i), line, (expected ? expected : "(null)")); #define TEST_FGETLINE(i, buf, size, contents, line1, line2, line3) \ if (!(file = fopen(filename, "wb"))) \ ++errors, printf("Test%d: failed to run test: failed to create test file\n", (i)); \ else \ { \ if (fwrite((contents), 1, strlen(contents), file) != strlen(contents)) \ ++errors, printf("Test%d: failed to run test: failed to write to test file\n", (i)); \ else \ { \ fclose(file); \ if (!(file = fopen(filename, "r"))) \ ++errors, printf("Test%d: failed to run test: failed to open test file for reading\n", (i)); \ else \ { \ CHECK_FGETLINE((i), (size), (line1)) \ CHECK_FGETLINE((i), (size), (line2)) \ CHECK_FGETLINE((i), (size), (line3)) \ if (fgetline(buf, BUFSIZ, file)) \ ++errors, printf("Test%d: fgetline() failed to return NULL at end of file\n", (i)); \ } \ } \ fclose(file); \ unlink(filename); \ } TEST_FGETLINE(7, line, BUFSIZ, "abc\ndef\r\nghi\r", "abc\n", "def\n", "ghi\n") TEST_FGETLINE(8, line, BUFSIZ, "abc\rdef\nghi\r\n", "abc\n", "def\n", "ghi\n") TEST_FGETLINE(9, line, BUFSIZ, "abc\r\ndef\rghi\n", "abc\n", "def\n", "ghi\n") TEST_FGETLINE(10, line, BUFSIZ, "abc\ndef\rghi", "abc\n", "def\n", "ghi") TEST_FGETLINE(11, line, BUFSIZ, "", (char *)NULL, (char *)NULL, (char *)NULL) TEST_FGETLINE(12, line, 5, "abc", "abc", (char *)NULL, (char *)NULL) TEST_FGETLINE(13, line, 5, "abc\n", "abc\n", (char *)NULL, (char *)NULL) TEST_FGETLINE(14, line, 5, "abc\r\n", "abc\n", (char *)NULL, (char *)NULL) TEST_FGETLINE(15, line, 5, "abc\r", "abc\n", (char *)NULL, (char *)NULL) TEST_FGETLINE(16, line, 3, "abc\r", "ab", "c\n", (char *)NULL) TEST_FGETLINE(17, NULL, 0, "abc\r", (char *)NULL, (char *)NULL, (char *)NULL) /* Test read_timeout() and write_timeout() */ if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, S_IRUSR | S_IWUSR)) == -1) ++errors, printf("Test19: failed to create %s (%s)\n", filename, strerror(errno)); else { char buf[12] = "0123456789\n"; if (write_timeout(fd, 1, 0) == -1) ++errors, printf("Test18: write_timeout(fd, 1, 0) failed (%s)\n", strerror(errno)); else if (write(fd, buf, 11) != 11) ++errors, printf("Test18: write(fd, \"0123456789\\n\", 11) failed (%s)\n", strerror(errno)); else { close(fd); if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) == -1) ++errors, printf("Test19: failed to open %s for reading (%s)\n", filename, strerror(errno)); else if (read_timeout(fd, 1, 0) == -1) ++errors, printf("Test19: read_timeout(fd, 1, 0) failed (%s)\n", strerror(errno)); else if (read(fd, buf, 11) != 11) ++errors, printf("Test19: read(fd) failed (%s)\n", strerror(errno)); } close(fd); } unlink(filename); /* Test error handling */ #define TEST_ERR(i, func) \ if ((func) != -1) \ ++errors, printf("Test%d: %s failed to return -1\n", (i), (#func)); \ else if (errno != EINVAL) \ ++errors, printf("Test%d: %s failed (errno = %s, not %s)\n", (i), (#func), strerror(errno), strerror(EINVAL)); TEST_ERR(20, read_timeout(-1, 0, 0)) TEST_ERR(21, read_timeout(0, -1, 0)) TEST_ERR(22, read_timeout(0, 0, -1)) TEST_ERR(23, write_timeout(-1, 0, 0)) TEST_ERR(24, write_timeout(0, -1, 0)) TEST_ERR(25, write_timeout(0, 0, -1)) TEST_ERR(26, rw_timeout(-1, 0, 0)) TEST_ERR(27, rw_timeout(0, -1, 0)) TEST_ERR(28, rw_timeout(0, 0, -1)) TEST_ERR(29, nap(-1, 0)) TEST_ERR(30, nap(0, -1)) if (errors) printf("%d/30 tests failed\n", errors); else printf("All tests passed\n"); #ifndef HAVE_FCNTL_THAT_CAN_LOCK_FIFOS printf("\n"); printf(" Note: Some systems (e.g. FreeBSD) can't lock fifos so fifo_open()\n"); printf(" can't guarantee a unique reader.\n"); #endif return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }
int fifo_open(const char *path, mode_t mode, int lock, int *writefd) { struct stat status[1]; int rfd, wfd, mine = 0; /* Don't open the fifo for reading twice. */ switch (fifo_has_reader(path, 1)) { case 1: return set_errno(EADDRINUSE); case -1: return -1; } /* Create the fifo. */ if (mkfifo(path, mode) != -1) mine = 1; else if (errno != EEXIST) return -1; /* ** Open the fifo for non-blocking read only. ** This prevents blocking while waiting for a ** writer process. We are about to supply our ** own writer. */ if ((rfd = open(path, O_RDONLY | O_NONBLOCK)) == -1) { if (mine) unlink(path); return -1; } /* ** A sanity check to make sure that what we have just ** opened is really a fifo. Someone may have just replaced ** the fifo with a file between fifo_has_reader and here. */ if (fstat(rfd, status) == -1 || S_ISFIFO(status->st_mode) == 0) { if (mine) unlink(path); close(rfd); return -1; } /* ** Open the fifo for write only and leave this fd open. ** This guarantees that there is always at least one ** writer process. This prevents EOF indications being ** returned from read() when there are no other writer ** processes. ** ** Just opening the fifo "rw" should work but it's undefined ** by POSIX. */ if ((wfd = open(path, O_WRONLY)) == -1) { if (mine) unlink(path); close(rfd); return -1; } /* ** Exclusively lock the fifo to prevent two invocations ** deciding that there's no reader and opening this fifo ** at the same time. ** ** Note: some systems (e.g. FreeBSD, Mac OS X) can't lock fifos :( */ /* On MacOSX-10.6 these are different numbers */ #ifndef ENOTSUP #define ENOTSUP EOPNOTSUPP #endif if (lock && fcntl_lock(wfd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1 && errno != EOPNOTSUPP && errno != ENOTSUP && errno != EBADF) { if (mine) unlink(path); close(rfd); close(wfd); return (errno == EACCES) ? set_errno(EADDRINUSE) : -1; } /* A sanity test on the write descriptor we have just opened and locked. */ if (fstat(wfd, status) == -1 || S_ISFIFO(status->st_mode) == 0) { if (mine) unlink(path); close(rfd); close(wfd); return -1; } /* Now put the reader into blocking mode. */ if (nonblock_off(rfd) == -1) { if (mine) unlink(path); close(rfd); close(wfd); return -1; } /* ** Flaw: If someone unceremoniously unlinks our fifo, we won't know ** about it and nothing will stop another invocation from creating a new ** fifo and handling it. This process would sleep forever in select(). */ if (writefd) *writefd = wfd; return rfd; }
/* return the pid in a pidfile. return 0 if the process (or pidfile) does not exist */ pid_t pidfile_pid(const char *program_name) { int fd; char pidstr[20]; pid_t pid; unsigned int ret; char *name; const char *short_configfile; char * pidFile; /* Add a suffix to the program name if this is a process with a * none default configuration file name. */ if (strcmp( CONFIGFILE, get_dyn_CONFIGFILE()) == 0) { name = SMB_STRDUP(program_name); } else { short_configfile = strrchr( get_dyn_CONFIGFILE(), '/'); if (short_configfile == NULL) { /* conf file in current directory */ short_configfile = get_dyn_CONFIGFILE(); } else { /* full/relative path provided */ short_configfile++; } if (asprintf(&name, "%s-%s", program_name, short_configfile) == -1) { smb_panic("asprintf failed"); } } if (asprintf(&pidFile, "%s/%s.pid", lp_piddir(), name) == -1) { SAFE_FREE(name); return 0; } SAFE_FREE(name); fd = open(pidFile, O_NONBLOCK | O_RDONLY, 0644); if (fd == -1) { SAFE_FREE(pidFile); return 0; } ZERO_ARRAY(pidstr); if (read(fd, pidstr, sizeof(pidstr)-1) <= 0) { goto noproc; } ret = atoi(pidstr); if (ret == 0) { /* Obviously we had some garbage in the pidfile... */ DEBUG(1, ("Could not parse contents of pidfile %s\n", pidFile)); goto noproc; } pid = (pid_t)ret; if (!process_exists_by_pid(pid)) { goto noproc; } if (fcntl_lock(fd,F_SETLK,0,1,F_RDLCK)) { /* we could get the lock - it can't be a Samba process */ goto noproc; } SAFE_FREE(pidFile); close(fd); return (pid_t)ret; noproc: close(fd); unlink(pidFile); SAFE_FREE(pidFile); return 0; }
int vfswrap_lock(char *fsp, int fd, int op, long long offset, long long count, int type) { return fcntl_lock(fd, op, offset, count, type); }