// // aegis - project change supervisor // Copyright (C) 1991-1999, 2001-2008 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // The HURD does not yet have POSIX semantics for its locks. You can // lock the whole file (and our lock file has zero length) but not parts // of the file. // #ifdef __hurd__ #define WHOLE_FILE_LOCKS_ONLY #endif // // Define this symbol if you want the lock poriority scheme, so // that integrate pass has a guarantee of succeeeding eventually. // (Without it, aeipass can be blocked indefinitely by rolling builds // by multiple developers.) // #define LOCK_PRIORITY_SCHEME // // how many bits of the hash to use in the mux // #define BITS 16 #define BITS_SIZE (1L << BITS) #define BITS_MASK (BITS_SIZE - 1) static quit_action_lock quitter; enum lock_ty { lock_master, lock_gstate, lock_MAX }; enum lock_mux_ty { lock_mux_baseline_priority, lock_mux_ustate, lock_mux_pstate, lock_mux_cstate, lock_mux_baseline, lock_mux_history, lock_mux_MAX }; static struct flock flock_zero; struct lock_place_ty { lock_place_ty() : location(flock_zero), release_immediately(0), callback(0), callback_arg(0) { } struct flock location; int release_immediately; lock_callback_ty callback; void *callback_arg; }; static string_ty *path; static size_t nplaces; static size_t nplaces_max; static lock_place_ty *place; static int fd = -1; static long magic; static int quitregd; // // Values for the lock_prepare::exclusive argument // #define LOCK_PREP_SHARED 0 #define LOCK_PREP_EXCLUSIVE 1 #define LOCK_PREP_PRIORITY 2 #define LOCK_PREP_EXCL_PRIO (LOCK_PREP_EXCLUSIVE | LOCK_PREP_PRIORITY) static void flock_construct(struct flock *p, int type, long start, long length) { // // the static zero variable is here because some systems have extra // fields in the flock structure. These fields are frequently // undocumented, and yet these systems give "EINVAL" if the // undocumented fields are not zero! // *p = flock_zero; assert(start >= 0); assert(length >= 0); p->l_type = type; p->l_whence = SEEK_SET; #ifdef WHOLE_FILE_LOCKS_ONLY // // The HURD does not yet have POSIX semantics for its locks. You // can lock the whole file (and our lock file has zero length) but // not parts of the file... and, I assume, not parts of the file // past the end of the file. // if (type != F_UNLCK) p->l_type = F_WRLCK; p->l_start = 0; p->l_len = 0; #else p->l_start = start; p->l_len = length; #endif p->l_pid = 0; } static int flock_equal(struct flock *p1, struct flock *p2) { return ( p1->l_type == p2->l_type && p1->l_start == p2->l_start && p1->l_len == p2->l_len ); } static void lock_prepare(long start, long length, int exclusive, lock_callback_ty callback, void *arg) { size_t j; lock_place_ty p; // // construct the lock structure // trace(("lock_prepare(start = %ld, length = %ld, excl = %d)\n{\n", start, length, exclusive)); #ifdef WHOLE_FILE_LOCKS_ONLY if (exclusive & LOCK_PREP_PRIORITY); { trace(("}\n")); return; } #endif assert(start > lock_master); assert(length > 0); flock_construct ( &p.location, ((exclusive & LOCK_PREP_EXCLUSIVE) ? F_WRLCK : F_RDLCK), start, length ); p.release_immediately = (exclusive & LOCK_PREP_PRIORITY); p.callback = callback; p.callback_arg = arg; // // if we have already got this one, don't add it to the list. // // While most systems are tolerant of asking for a lock twice, // some are not equally tolerant of releasing it more then once. // for (j = 0; j < nplaces; ++j) { if (flock_equal(&p.location, &place[j].location)) { trace(("}\n")); return; } } // // Append the lock to the list of // locks to be taken. // // The locks are taken in one go // to avoid deadlocks. // if (nplaces >= nplaces_max) { nplaces_max = nplaces_max * 2 + 4; lock_place_ty *new_place = new lock_place_ty [nplaces_max]; for (size_t k = 0; k < nplaces; ++k) new_place[k] = place[k]; delete [] place; place = new_place; } place[nplaces++] = p; trace(("}\n")); } void lock_prepare_gstate(lock_callback_ty callback, void *arg) { trace(("lock_prepare_gstate()\n{\n")); lock_prepare((long)lock_gstate, 1L, 1, callback, arg); trace(("}\n")); } static void lock_prepare_mux(lock_mux_ty lock_mux, long n, int exclusive, lock_callback_ty callback, void *arg) { trace(("lock_prepare_mux(lock_mux = %d, n = %ld, excl = %d)\n{\n", lock_mux, n, exclusive)); lock_prepare ( ((lock_mux + 1L) << BITS) + (n & BITS_MASK), 1L, exclusive, callback, arg ); trace(("}\n")); } static void lock_prepare_mux_all(lock_mux_ty lock_mux, int exclusive, lock_callback_ty callback, void *arg) { trace(("lock_prepare_mux_all(lock_mux = %d)\n{\n", lock_mux)); lock_prepare ( ((lock_mux + 1L) << BITS), BITS_SIZE, exclusive, callback, arg ); trace(("}\n")); } void lock_prepare_pstate(string_ty *s, lock_callback_ty callback, void *arg) { trace(("lock_prepare_pstate(s = \"%s\")\n{\n", s->str_text)); lock_prepare_mux ( lock_mux_pstate, (long)s->str_hash, LOCK_PREP_EXCLUSIVE, callback, arg ); trace(("}\n")); } void lock_prepare_baseline_read(string_ty *s, lock_callback_ty callback, void *arg) { trace(("lock_prepare_pstate(s = \"%s\")\n{\n", s->str_text)); #ifdef LOCK_PRIORITY_SCHEME lock_prepare_mux ( lock_mux_baseline_priority, (long)s->str_hash, LOCK_PREP_EXCL_PRIO, callback, arg ); #endif lock_prepare_mux ( lock_mux_baseline, (long)s->str_hash, LOCK_PREP_SHARED, callback, arg ); trace(("}\n")); } void lock_prepare_baseline_write(string_ty *s, lock_callback_ty callback, void *arg) { trace(("lock_prepare_pstate(s = \"%s\")\n{\n", s->str_text)); #ifdef LOCK_PRIORITY_SCHEME lock_prepare_mux ( lock_mux_baseline_priority, (long)s->str_hash, LOCK_PREP_EXCLUSIVE, callback, arg ); #endif lock_prepare_mux ( lock_mux_baseline, (long)s->str_hash, LOCK_PREP_EXCLUSIVE, callback, arg ); trace(("}\n")); } void lock_prepare_history(string_ty *s, lock_callback_ty callback, void *arg) { trace(("lock_prepare_pstate(s = \"%s\")\n{\n", s->str_text)); lock_prepare_mux ( lock_mux_history, (long)s->str_hash, LOCK_PREP_EXCLUSIVE, callback, arg ); trace(("}\n")); } void lock_prepare_ustate(int uid, lock_callback_ty callback, void *arg) { trace(("lock_prepare_ustate()\n{\n")); lock_prepare_mux ( lock_mux_ustate, (long)uid, LOCK_PREP_EXCLUSIVE, callback, arg ); trace(("}\n")); } void lock_prepare_ustate_all(lock_callback_ty callback, void *arg) { trace(("lock_prepare_ustate_all()\n{\n")); lock_prepare_mux_all(lock_mux_ustate, LOCK_PREP_EXCLUSIVE, callback, arg); trace(("}\n")); } void lock_prepare_cstate(string_ty *project_name, long change_number, lock_callback_ty callback, void *arg) { trace(("lock_prepare_cstate(project_name = \"%s\", change_number = %ld)\n" "{\n", project_name->str_text, change_number)); lock_prepare_mux ( lock_mux_cstate, change_number + project_name->str_hash, LOCK_PREP_EXCLUSIVE, callback, arg ); trace(("}\n")); } static const char * flock_type_string(int n) { switch (n) { case F_RDLCK: return "F_RDLCK"; case F_WRLCK: return "F_WRLCK"; case F_UNLCK: return "F_UNLCK"; } return "unknown"; } static const char * flock_whence_string(int n) { switch (n) { case SEEK_SET: return "SEEK_SET"; case SEEK_CUR: return "SEEK_CUR"; case SEEK_END: return "SEEK_END"; } return "unknown"; } static char * flock_string(struct flock *p) { static char buffer[120]; snprintf ( buffer, sizeof(buffer), "&{type = %s, whence = %s, start = %ld, len = %ld, pid = %d}", flock_type_string(p->l_type), flock_whence_string(p->l_whence), (long)p->l_start, (long)p->l_len, p->l_pid ); return buffer; } static const char * lock_description(struct flock *p) { switch (p->l_start) { case lock_master: return "master"; case lock_gstate: return "global state"; } switch ((p->l_start >> BITS) - 1) { case lock_mux_baseline_priority: return "baseline priority"; case lock_mux_ustate: return "user"; case lock_mux_cstate: return "change"; case lock_mux_pstate: return "project"; case lock_mux_baseline: return "baseline"; case lock_mux_history: return "history"; } return "unknown"; } void lock_quitter() { if (fd >= 0) { while (os_become_active()) os_become_undo(); lock_release(); } } void lock_take(void) { struct flock p; size_t j; int nsecs; int wait_for_locks; // // get the file descriptor of the lock file // // If it isn't there, create it. If it is there, truncate it (prevent // abuse of universally writable file). // trace(("lock_take()\n{\n")); assert(fd < 0); assert(nplaces); if (!quitregd) { quitregd = 1; quit_register(quitter); } if (!path) path = gonzo_lockpath_get(); // // See if we will wait for locks. // wait_for_locks = user_ty::create()->lock_wait(); gonzo_become(); #if defined(__CYGWIN__) || defined(__hpux__) // // There isn't a major security problem here, because the user // can't park weird files here, and we truncate them anyway. // Also, the directory is supposed to be read-only for just // about everyone. // // CYGWIN is only really useful in single user, so that isn't // an issue. // // The problem comes when you are using HP/UX over NFS. // It gets errors like "no locks available" if the file isn't // world writable. I could understand if we were the wrong user // id (many NFS implementations have kittens when you open file // file as one user, and the read or write it as another user) // but we are using the same user id as when we opened the file. // // But making the file world-writable means we have a potential // denial of service problem. A process could take a lock in // the file, preventing all Aegis processes from obtaining locks // they need. // // Unfortunately, the #ifdef __hpux__ probably isn't enough, // because you need a world writable file if there are *any* // HP/UX clients, and the file will probably be created by some // other (inevitably, better) UNIX. // fd = glue_open(path->str_text, O_RDWR | O_CREAT | O_TRUNC, 0666); #else fd = glue_open(path->str_text, O_RDWR | O_CREAT | O_TRUNC, 0600); #endif if (fd < 0) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); fatal_intl(scp, i18n("open $filename: $errno")); // NOTREACHED sub_context_delete(scp); } trace_int(fd); // // make sure the file is closed when a child exec's // #ifndef CONF_NO_seteuid int flags = 0; if (fcntl(fd, F_GETFD, &flags)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); fatal_intl(scp, i18n("fcntl(\"$filename\", F_GETFD): $errno")); // NOTREACHED sub_context_delete(scp); } flags |= 1; if (fcntl(fd, F_SETFD, flags)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_long(scp, "Argument", flags); fatal_intl(scp, i18n("fcntl(\"$filename\", F_SETFD, $arg): $errno")); // NOTREACHED sub_context_delete(scp); } trace(("mark\n")); #endif // // Block for the master lock. // Take the locks we really want. // If we got them, release master and return. // If did not get all of the locks, // release the locks we could get and then the master. // Block on the lock we could not get, // release that lock and start again. // // Unfortunately, this scheme isn't fair // (i.e. no guarantee of success in finite time) // it would be nice if I could atomically lock (and block) // an entire vector of locks. (vfcntl like berkeley's vwrite?) // for (;;) { // // Cope with interrupts while trying to obtain locks. // os_interrupt_cope(); // // get the master lock // (block if necessary, // it should never be held for long) // trace(("mark\n")); #ifndef WHOLE_FILE_LOCKS_ONLY flock_construct(&p, F_WRLCK, (long)lock_master, 1L); if (glue_fcntl(fd, F_SETLKW, &p)) { sub_context_ty *scp; int errno_old; errno_old = errno; if (errno_old == EINTR) os_interrupt_cope(); scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&p)); fatal_intl ( scp, i18n("fcntl(\"$filename\", F_SETLKW, $arg): $errno") ); // NOTREACHED sub_context_delete(scp); } #endif // // get each of the locks we really wanted // for (j = 0; j < nplaces; ++j) { trace(("mark\n")); #ifdef WHOLE_FILE_LOCKS_ONLY assert(!place[j].release_immediately); #endif p = place[j].location; // yes, copy it if (glue_fcntl(fd, F_SETLK, &p)) { if (errno != EACCES && errno != EAGAIN) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&p)); fatal_intl ( scp, i18n("fcntl(\"$filename\", F_SETLK, $arg): $errno") ); // NOTREACHED sub_context_delete(scp); } break; } } // // return if were successful. // if (j >= nplaces) { trace(("mark\n")); #ifndef WHOLE_FILE_LOCKS_ONLY flock_construct(&p, F_UNLCK, (long)lock_master, 1L); if (glue_fcntl(fd, F_SETLKW, &p)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&p)); fatal_intl ( scp, i18n("fcntl(\"$filename\", F_SETLKW, $arg): $errno") ); // NOTREACHED sub_context_delete(scp); } // // Unlock any of the "release immediately" locks. // (These are used to gain a modicum of fairness.) // for (j = 0; j < nplaces; ++j) { if (!place[j].release_immediately) continue; trace(("mark\n")); p = place[j].location; // yes, copy it p.l_type = F_UNLCK; if (glue_fcntl(fd, F_SETLKW, &p)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&p)); fatal_intl ( scp, i18n("fcntl(\"$filename\", F_SETLKW, $arg): $errno") ); // NOTREACHED sub_context_delete(scp); } } #endif // WHOLE_FILE_LOCKS_ONLY break; } // // give all the locks back // that we got so far // flock_construct(&p, F_UNLCK, 0L, 0L); if (glue_fcntl(fd, F_SETLKW, &p)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&p)); fatal_intl ( scp, i18n("fcntl(\"$filename\", F_SETLKW, $arg): $errno") ); } // // Cope with interrupts while trying to obtain locks. // os_interrupt_cope(); trace(("mark\n")); // // verbose message about why we are blocking // if (place[j].callback) { place[j].callback(place[j].callback_arg); if (!wait_for_locks) { sub_context_ty *scp; scp = sub_context_new(); // should never be reached sub_var_set_charstar(scp, "Name", lock_description(&p)); fatal_intl(scp, i18n("$name lock not available")); sub_context_delete(scp); } } else { sub_context_ty *scp; scp = sub_context_new(); p = place[j].location; // yes, copy it sub_var_set_charstar(scp, "Name", lock_description(&p)); if (wait_for_locks) verbose_intl(scp, i18n("waiting for $name lock")); else fatal_intl(scp, i18n("$name lock not available")); sub_context_delete(scp); } // // sleep for a random number of seconds. // This is necessary, because it is possible for two // processes to be trying for the same lock // and they alternate in the next wait. // nsecs = r250() % 5; if (nsecs) { sleep(nsecs); os_interrupt_cope(); } // // block on the lock that stopped us before // if (j > 0 && place[j - 1].release_immediately) { // // If the lock is preceeded by a priority lock, // take that too, to stop them getting through. // We can probably get it without waiting, // because we already got it once before. // trace(("mark\n")); p = place[j - 1].location; // yes, copy it if (glue_fcntl(fd, F_SETLKW, &p)) { sub_context_ty *scp; int errno_old; errno_old = errno; if (errno_old == EINTR) os_interrupt_cope(); scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&p)); fatal_intl ( scp, i18n("fcntl(\"$filename\", F_SETLKW, $arg): $errno") ); // NOTREACHED sub_context_delete(scp); } } trace(("mark\n")); p = place[j].location; // yes, copy it if (glue_fcntl(fd, F_SETLKW, &p)) { sub_context_ty *scp; int errno_old; errno_old = errno; if (errno_old == EINTR) os_interrupt_cope(); scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&p)); fatal_intl ( scp, i18n("fcntl(\"$filename\", F_SETLKW, $arg): $errno") ); // NOTREACHED sub_context_delete(scp); } // // and then release it, // before trying all over again. // flock_construct(&p, F_UNLCK, 0L, 0L); if (glue_fcntl(fd, F_SETLKW, &p)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&p)); fatal_intl ( scp, i18n("fcntl(\"$filename\", F_SETLKW, $arg): $errno") ); // NOTREACHED sub_context_delete(scp); } trace(("mark\n")); } gonzo_become_undo(); magic++; // // Reset the idea of when it is, we pobably needed to sleep. // The timestamps need to be dates *after* the lock was obtained. // now_clear(); trace(("}\n")); } void lock_release(void) { struct flock p; int fildes; int err; // // set the file descriptor to -1 // so that we will not be invoked again should // a fatal error happen here. // trace(("lock_release()\n{\n")); trace_int(fd); assert(fd >= 0); assert(nplaces); assert(place); assert(path); gonzo_become(); fildes = fd; fd = -1; // // Release all of the locks. // // This should be unnecessary because we close the file anyway, // but some operating systems hang onto them if we don't. // flock_construct(&p, F_UNLCK, 0L, 0L); err = glue_fcntl(fildes, F_SETLKW, &p); #ifdef __CYGWIN__ if (err && errno == EACCES) { // // For some reason Cygwin gives an error if you attempt // to release all locks when you have no locks taken. // Unix implementations, on the other hand, simply // return success. // err = 0; } #endif if (err) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&p)); fatal_intl(scp, i18n("fcntl(\"$filename\", F_SETLKW, $arg): $errno")); // NOTREACHED sub_context_delete(scp); } // // close the file // (this *should* have been enough) // glue_close(fildes); gonzo_become_undo(); nplaces = 0; nplaces_max = 0; delete [] place; place = 0; trace(("}\n")); } long lock_magic(void) { return magic; } static void lock_walk_hunt(long min, long max, lock_walk_callback callback) { struct flock file_lock; int j; lock_walk_found found; // // look for a lock in the given range // file_lock.l_type = F_WRLCK; file_lock.l_whence = SEEK_SET; file_lock.l_start = min; file_lock.l_len = max - min; file_lock.l_pid = 0; gonzo_become(); if (glue_fcntl(fd, F_GETLK, &file_lock)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_charstar(scp, "Argument", flock_string(&file_lock)); fatal_intl(scp, i18n("fcntl(\"$filename\", F_GETLK, $arg): $errno")); // NOTREACHED sub_context_delete(scp); } gonzo_become_undo(); if (file_lock.l_type == F_UNLCK) return; // // Some operating systems return more than we asked for. // if (file_lock.l_start < min) { file_lock.l_len -= (min - file_lock.l_start); file_lock.l_start = min; } if (file_lock.l_len <= 0) return; if (file_lock.l_start + file_lock.l_len > max) file_lock.l_len = max - file_lock.l_start; if (file_lock.l_len <= 0) return; // // aegis only uses byte locks, // so multi-byte ranges are separate locks // for (j = 0; j < file_lock.l_len; ++j) { // // figure the name and address // found.address = file_lock.l_start + j; found.subset = 0; switch (found.address) { case lock_master: found.name = lock_walk_name_master; break; case lock_gstate: found.name = lock_walk_name_gstate; break; default: switch ((found.address >> BITS) - 1) { case lock_mux_ustate: found.name = lock_walk_name_ustate; found.subset = found.address & BITS_MASK; break; case lock_mux_pstate: found.name = lock_walk_name_pstate; found.subset = found.address & BITS_MASK; break; case lock_mux_cstate: found.name = lock_walk_name_cstate; found.subset = found.address & BITS_MASK; break; case lock_mux_baseline_priority: found.name = lock_walk_name_baseline_priority; found.subset = found.address & BITS_MASK; break; case lock_mux_baseline: found.name = lock_walk_name_baseline; found.subset = found.address & BITS_MASK; break; case lock_mux_history: found.name = lock_walk_name_history; found.subset = found.address & BITS_MASK; break; default: found.name = lock_walk_name_unknown; break; } } // // figure the type // switch (file_lock.l_type) { case F_RDLCK: found.type = lock_walk_type_shared; break; case F_WRLCK: found.type = lock_walk_type_exclusive; break; default: found.type = lock_walk_type_unknown; break; } // // Process holding the lock. // Workout if it is local or remote. // found.pid = file_lock.l_pid; if (kill(found.pid, 0)) { switch (errno) { default: nfatal("kill(%d, 0)", found.pid); case EPERM: found.pid_is_local = 1; break; case ESRCH: found.pid_is_local = 0; break; } } else found.pid_is_local = 1; // // do something with it // callback(&found); } // // look for more locks on either side // if (min < file_lock.l_start) lock_walk_hunt(min, file_lock.l_start, callback); if (file_lock.l_start + file_lock.l_len < max) lock_walk_hunt(file_lock.l_start + file_lock.l_len, max, callback); } void lock_walk(lock_walk_callback callback) { trace(("lock_walk()\n{\n")); assert(fd < 0); assert(!nplaces); if (!quitregd) { quitregd = 1; quit_register(quitter); } if (!path) path = gonzo_lockpath_get(); gonzo_become(); fd = glue_open(path->str_text, O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd < 0) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); fatal_intl(scp, i18n("open $filename: $errno")); // NOTREACHED sub_context_delete(scp); } trace_int(fd); // // make sure the file is closed when a child exec's // #ifndef CONF_NO_seteuid int flags = 0; if (fcntl(fd, F_GETFD, &flags)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); fatal_intl(scp, i18n("fcntl(\"$filename\", F_GETFD): $errno")); // NOTREACHED sub_context_delete(scp); } flags |= 1; if (fcntl(fd, F_SETFD, flags)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); sub_var_set_long(scp, "Argument", flags); fatal_intl(scp, i18n("fcntl(\"$filename\", F_SETFD, $arg): $errno")); // NOTREACHED sub_context_delete(scp); } trace(("mark\n")); #endif gonzo_become_undo(); // // chase all of the locks // lock_walk_hunt(0L, ((lock_mux_MAX + 1) << BITS), callback); // // close the file // gonzo_become(); trace_int(fd); assert(fd >= 0); assert(path); int fildes = fd; fd = -1; glue_close(fildes); gonzo_become_undo(); trace(("}\n")); } void lock_release_child(void) { #ifdef glue_close // // Only do this if and only if we are NOT using a proxy. // if (fd >= 0) close(fd); fd = -1; #endif } bool lock_active() { return (fd >= 0); }