command is killed by a signal, because people expect such
behavior from Sendmail. File: global/pipe_command.c.
-20001123-6
+20001123-30
Feature: mailbox locking is now configurable. The configuration
parameter name is "mailbox_delivery_lock". Depending on
old sun_mailtool_compatibility parameter is being phased
out (it just turns off flock/fcntl locks). It still works,
but a warning is logged as a reminder that it goes away.
+
+20001202
+
+ Feature: specify "smtp_never_send_ehlo = no" to disable
+ ESMTP. Someone asked for this long ago. Files: smtp/smtp.c,
+ smtp/smtp_proto.c.
* case of trouble.
*/
if (deliver_flock(vstream_fileno(log), INTERNAL_LOCK, (VSTRING *) 0) < 0)
- msg_fatal("lock file %s %s: %m", service, queue_id);
+ msg_fatal("lock file %s %s: %m", service, queue_id);
/*
* Now, go for it. Append a record. Truncate the log to the original
/* int lock_style;
/* VSTRING *why;
/* DESCRIPTION
-/* deliver_flock() sets one exclusive kernel lock on an open file
-/* for the purpose of mail delivery. It attempts to acquire
-/* the exclusive lock several times before giving up.
+/* deliver_flock() sets one exclusive kernel lock on an open file,
+/* for example in order to deliver mail.
+/* It performs several non-blocking attempts to acquire an exclusive
+/* lock before giving up.
/*
/* Arguments:
/* .IP fd
/* deliver_flock - lock open file for mail delivery*/
-int deliver_flock(int fd, int lock_style, VSTRING *why)
+int deliver_flock(int fd, int lock_style, VSTRING * why)
{
int i;
- for (i = 0; /* void */ ; i++) {
+ for (i = 1; /* void */ ; i++) {
+ if (myflock(fd, lock_style,
+ MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) == 0)
+ return (0);
if (i >= var_flock_tries)
break;
- if (i > 0)
- sleep(var_flock_delay);
- if (myflock(fd, lock_style, MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) == 0)
- return (0);
+ sleep(var_flock_delay);
}
if (why)
vstring_sprintf(why, "unable to lock for exclusive access: %m");
/*
* Attempt to create the lock. This code relies on O_EXCL | O_CREAT
- * to not follow symlinks.
+ * to not follow symlinks. With NFS file systems this operation can
+ * at the same time succeed and fail with errno of EEXIST.
*/
if ((fd = open(lock_file, O_WRONLY | O_EXCL | O_CREAT, 0)) >= 0) {
close(fd);
#define DEF_SMTP_ALWAYS_EHLO 0
extern bool var_smtp_always_ehlo;
+#define VAR_SMTP_NEVER_EHLO "smtp_never_send_ehlo"
+#define DEF_SMTP_NEVER_EHLO 0
+extern bool var_smtp_never_ehlo;
+
#define VAR_SMTP_BIND_ADDR "smtp_bind_address"
#define DEF_SMTP_BIND_ADDR ""
extern char *var_smtp_bind_addr;
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20001127"
+#define DEF_MAIL_VERSION "Snapshot-20001203"
extern char *var_mail_version;
/* LICENSE
/* file ownership will succeed only if the process runs with
/* adequate effective privileges.
/* The \fBlock_style\fR argument specifies a lock style from
-/* mbox_lock_mask(). Kernel locks are applied to regular files only.
+/* mbox_lock_mask(). Locks are applied to regular files only.
/* The result is a handle that must be destroyed by mbox_release().
/*
/* mbox_release() releases the named mailbox. It is up to the
MBOX *mp;
int locked = 0;
VSTREAM *fp;
-
- /*
- * Create dotlock file. This locking method does not work well over NFS:
- * creating files atomically is a problem, and a successful operation can
- * fail with EEXIST.
- *
- * If file.lock can't be created, ignore the problem if the application says
- * so. We need this so that Postfix can deliver as unprivileged user to
- * /dev/null style aliases. Alternatively, we could open the file first,
- * and dot-lock the file only if it is a regular file, just like we do
- * with kernel locks.
- */
- if (lock_style & MBOX_DOT_LOCK) {
- if (dot_lockfile(path, why) == 0) {
- locked |= MBOX_DOT_LOCK;
- } else {
- if (errno == EEXIST) {
- errno = EAGAIN;
- return (0);
- }
- if ((lock_style & MBOX_DOT_LOCK_MAY_FAIL) == 0) {
- return (0);
- }
- }
- }
+ int saved_errno;
/*
* Open or create the target file. In case of a privileged open, the
- * privileged user may be attacked through an unsafe parent directory. In
- * case of an unprivileged open, the mail system may be attacked by a
- * malicious user-specified path, and the unprivileged user may be
- * attacked through an unsafe parent directory. Open non-blocking to fend
- * off attacks involving FIFOs and other weird targets.
+ * privileged user may be attacked with hard/soft link tricks in an
+ * unsafe parent directory. In case of an unprivileged open, the mail
+ * system may be attacked by a malicious user-specified path, or the
+ * unprivileged user may be attacked with hard/soft link tricks in an
+ * unsafe parent directory. Open non-blocking to fend off attacks
+ * involving non-file targets.
+ *
+ * We open before locking, so that we can avoid attempts to dot-lock
+ * destinations such as /dev/null.
*/
if (st == 0)
st = &local_statbuf;
if ((fp = safe_open(path, flags, mode | O_NONBLOCK, st,
chown_uid, chown_gid, why)) == 0) {
- if (locked & MBOX_DOT_LOCK)
- dot_unlockfile(path);
return (0);
}
- non_blocking(vstream_fileno(fp), BLOCKING);
close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC);
/*
- * Acquire kernel locks, but only if the target is a regular file, in
- * case we're running on some overly pedantic system. flock() locks do
- * not work over NFS; fcntl() locks are supposed to work over NFS, but in
- * the real world, NFS lock daemons often have serious problems.
+ * If this is a regular file, create a dotlock file. This locking method
+ * does not work well over NFS, but it is better than some alternatives.
+ * With NFS, creating files atomically is a problem, and a successful
+ * operation can fail with EEXIST.
+ *
+ * If filename.lock can't be created for reasons other than "file exists",
+ * issue only a warning if the application says it is non-fatal. This is
+ * for bass-awkward compatibility with existing installations that
+ * deliver to files in non-writable directories.
+ *
+ * Alternatively, we could dot-lock the file before opening, but then we
+ * would be doing silly things like dot-locking /dev/null, something that
+ * an unprivileged user is not supposed to be able to do.
*/
-#define LOCK_FAIL(mbox_lock, myflock_style) ((lock_style & (mbox_lock)) != 0 \
- && deliver_flock(vstream_fileno(fp), (myflock_style), why) < 0)
-
- if (S_ISREG(st->st_mode)
- && (LOCK_FAIL(MBOX_FLOCK_LOCK, MYFLOCK_STYLE_FLOCK)
- || LOCK_FAIL(MBOX_FCNTL_LOCK, MYFLOCK_STYLE_FCNTL))) {
- if (myflock_locked(vstream_fileno(fp)))
+ if (S_ISREG(st->st_mode) && (lock_style & MBOX_DOT_LOCK)) {
+ if (dot_lockfile(path, why) == 0) {
+ locked |= MBOX_DOT_LOCK;
+ } else if (errno == EEXIST) {
errno = EAGAIN;
- if (locked & MBOX_DOT_LOCK)
- dot_unlockfile(path);
- return (0);
+ vstream_fclose(fp);
+ return (0);
+ } else if (lock_style & MBOX_DOT_LOCK_MAY_FAIL) {
+ msg_warn("%s", vstring_str(why));
+ } else {
+ vstream_fclose(fp);
+ return (0);
+ }
+ }
+
+ /*
+ * If this is a regular file, acquire kernel locks. flock() locks are not
+ * intended to work across a network; fcntl() locks are supposed to work
+ * over NFS, but in the real world, NFS lock daemons often have serious
+ * problems.
+ */
+#define HUNKY_DORY(lock_mask, myflock_style) ((lock_style & (lock_mask)) == 0 \
+ || deliver_flock(vstream_fileno(fp), (myflock_style), why) == 0)
+
+ if (S_ISREG(st->st_mode)) {
+ if (HUNKY_DORY(MBOX_FLOCK_LOCK, MYFLOCK_STYLE_FLOCK)
+ && HUNKY_DORY(MBOX_FCNTL_LOCK, MYFLOCK_STYLE_FCNTL)) {
+ locked |= lock_style;
+ } else {
+ saved_errno = errno;
+ if (locked & MBOX_DOT_LOCK)
+ dot_unlockfile(path);
+ vstream_fclose(fp);
+ errno = saved_errno;
+ return (0);
+ }
}
mp = (MBOX *) mymalloc(sizeof(*mp));
mp->path = mystrdup(path);
void mbox_release(MBOX *mp)
{
+
+ /*
+ * Unfortunately we can't close the stream, because on some file systems
+ * (AFS), the only way to find out if a file was written successfully is
+ * to close it, and therefore the close() operation is in the mail_copy()
+ * routine. If we really insist on owning the vstream member, then we
+ * should export appropriate methods that mail_copy() can use in order
+ * to manipulate a message stream.
+ */
if (mp->locked & MBOX_DOT_LOCK)
dot_unlockfile(mp->path);
myfree(mp->path);
struct stat st;
MBOX *mp;
VSTRING *why;
- int status;
+ int status = -1;
int copy_flags;
/*
/*
* As the specified user, open or create the file, lock it, and append
- * the message. XXX We may attempt to create a lockfile for /dev/null.
+ * the message.
*/
copy_flags = MAIL_COPY_MBOX;
if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0)
mp = mbox_open(path, O_APPEND | O_CREAT | O_WRONLY,
S_IRUSR | S_IWUSR, &st, -1, -1,
local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL, why);
- if (mp == 0) {
- status = (errno == EAGAIN ? defer_append : bounce_append)
+ if (mp != 0) {
+ if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+ vstream_fclose(mp->fp);
+ vstring_sprintf(why, "destination file is executable");
+ errno = 0;
+ } else {
+ status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
+ S_ISREG(st.st_mode) ? copy_flags :
+ (copy_flags & ~MAIL_COPY_TOFILE),
+ "\n", why);
+ }
+ mbox_release(mp);
+ }
+ set_eugid(var_owner_uid, var_owner_gid);
+
+ /*
+ * As the mail system, bounce, defer delivery, or report success.
+ */
+ if (status != 0) {
+ status = (errno == EAGAIN || errno == ENOSPC ?
+ defer_append : bounce_append)
(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
- "cannot access destination file %s: %s", path, STR(why));
- } else if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
- vstream_fclose(mp->fp);
- status = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
- "executable destination file %s", path);
- } else if (mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
- S_ISREG(st.st_mode) ? copy_flags : (copy_flags & ~MAIL_COPY_TOFILE),
- "\n", why)) {
- status = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
- "cannot append destination file %s: %s",
- path, STR(why));
+ "cannot append message to destination file %s: %s",
+ path, STR(why));
} else {
- status = sent(SENT_ATTR(state.msg_attr), "%s", path);
+ sent(SENT_ATTR(state.msg_attr), "%s", path);
}
- mbox_release(mp);
- set_eugid(var_owner_uid, var_owner_gid);
/*
* Clean up.
/* Global library. */
#include <mail_copy.h>
-#include <safe_open.h>
-#include <deliver_flock.h>
-#include <dot_lockfile.h>
#include <defer.h>
#include <sent.h>
#include <mypwd.h>
char *spool_dir;
char *mailbox;
VSTRING *why;
- MBOX *mp;
+ MBOX *mp;
int status;
int copy_flags;
VSTRING *biff;
set_eugid(spool_uid, spool_gid);
mp = mbox_open(mailbox, O_APPEND | O_WRONLY | O_CREAT,
- S_IRUSR | S_IWUSR, &st, chown_uid, chown_gid,
- local_mbox_lock_mask, why);
+ S_IRUSR | S_IWUSR, &st, chown_uid, chown_gid,
+ local_mbox_lock_mask, why);
if (mp != 0) {
if (spool_uid != usr_attr.uid || spool_gid != usr_attr.gid)
set_eugid(usr_attr.uid, usr_attr.gid);
if (S_ISREG(st.st_mode) == 0) {
vstream_fclose(mp->fp);
- vstring_sprintf(why, "file %s should be a regular file", mailbox);
+ vstring_sprintf(why, "destination is not a regular file");
errno = 0;
} else {
end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END);
}
set_eugid(var_owner_uid, var_owner_gid);
+ /*
+ * As the mail system, bounce, defer delivery, or report success.
+ */
if (status != 0) {
status = (errno == EAGAIN || errno == ENOSPC ?
defer_append : bounce_append)
(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
- "cannot access mailbox for user %s. %s",
- state.msg_attr.user, vstring_str(why));
+ "cannot access mailbox %s for user %s. %s",
+ mailbox, state.msg_attr.user, vstring_str(why));
} else {
sent(SENT_ATTR(state.msg_attr), "mailbox");
if (var_biff) {
vstring_free(biff);
}
}
+
+ /*
+ * Clean up.
+ */
myfree(mailbox);
vstring_free(why);
return (status);
if (test_lock)
exit(lock_fp ? 0 : 1);
if (lock_fp == 0)
- msg_fatal("%s", vstring_str(why));
+ msg_fatal("open lock file %s: %s",
+ vstring_str(lock_path), vstring_str(why));
vstream_fprintf(lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4,
(unsigned long) var_pid);
if (vstream_fflush(lock_fp))
why = vstring_alloc(1);
if ((multi_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
(struct stat *) 0, -1, -1, why)) == 0)
- msg_fatal("%s", vstring_str(why));
+ msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
close_on_exec(vstream_fileno(multi_server_lock), CLOSE_ON_EXEC);
myfree(lock_path);
vstring_free(why);
why = vstring_alloc(1);
if ((single_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
(struct stat *) 0, -1, -1, why)) == 0)
- msg_fatal("%s", vstring_str(why));
+ msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
close_on_exec(vstream_fileno(single_server_lock), CLOSE_ON_EXEC);
myfree(lock_path);
vstring_free(why);
why = vstring_alloc(1);
if ((trigger_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
(struct stat *) 0, -1, -1, why)) == 0)
- msg_fatal("%s", vstring_str(why));
+ msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
close_on_exec(vstream_fileno(trigger_server_lock), CLOSE_ON_EXEC);
myfree(lock_path);
vstring_free(why);
/* Problems are logged to the standard error stream. No output means
/* no problems were detected. Duplicate entries are skipped and are
/* flagged with a warning.
+/*
+/* \fBpostalias\fR terminates with zero exit status in case of success
+/* (including successful \fBpostmap -q\fR lookup) and terminates
+/* with non-zero exit status in case of failure.
/* BUGS
/* The "delete key" support is limited to one delete operation
/* per command invocation.
#include <vstream.h>
#include <msg_vstream.h>
#include <iostuff.h>
-#include <safe_open.h>
/* Global library. */
command = argv + optind + 1;
/*
- * Read the config file.
+ * Read the config file. The command line lock style can override the
+ * configured lock style.
*/
mail_conf_read();
lock_mask = mbox_lock_mask(lock_style ? lock_style :
if ((mp = mbox_open(folder, O_APPEND | O_WRONLY | O_CREAT,
S_IRUSR | S_IWUSR, (struct stat *) 0,
-1, -1, lock_mask, why)) == 0)
- msg_fatal("%s", vstring_str(why));
+ msg_fatal("open file %s: %s", folder, vstring_str(why));
/*
* Run the command. Remove the lock after completion.
*/
- for (count = 0; count < var_fork_tries; count++) {
- switch (pid = fork()) {
- case -1:
- msg_warn("fork %s: %m", command[0]);
- break;
- case 0:
- execvp(command[0], command);
- msg_fatal("execvp %s: %m", command[0]);
- default:
- if (waitpid(pid, &status, 0) < 0)
- msg_fatal("waitpid: %m");
+ for (count = 1; (pid = fork()) == -1; count++) {
+ msg_warn("fork %s: %m", command[0]);
+ if (count >= var_fork_tries) {
mbox_release(mp);
- exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
+ exit(EX_TEMPFAIL);
}
- if (count + 1 < var_fork_tries)
- sleep(var_fork_delay);
+ sleep(var_fork_delay);
+ }
+ switch (pid) {
+ case 0:
+ execvp(command[0], command);
+ msg_fatal("execvp %s: %m", command[0]);
+ default:
+ if (waitpid(pid, &status, 0) < 0)
+ msg_fatal("waitpid: %m");
+ mbox_release(mp);
+ exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1);
}
- mbox_release(mp);
- exit(EX_TEMPFAIL);
}
/* Problems and transactions are logged to the standard error
/* stream. No output means no problems. Duplicate entries are
/* skipped and are flagged with a warning.
+/*
+/* \fBpostmap\fR terminates with zero exit status in case of success
+/* (including successful \fBpostmap -q\fR lookup) and terminates
+/* with non-zero exit status in case of failure.
/* BUGS
/* The "delete key" support is limited to one delete operation
/* per command invocation.
/* or if a destination is unreachable.
/* .IP \fBignore_mx_lookup_error\fR
/* When a name server fails to respond to an MX query, search for an
-/* A record instead of assuming that the name server will recover.
+/* A record instead deferring mail delivery.
/* .IP \fBinet_interfaces\fR
/* The network interface addresses that this mail system receives
/* mail on. When any of those addresses appears in the list of mail
/* postmaster with transcripts of SMTP sessions with protocol errors.
/* .IP \fBsmtp_always_send_ehlo\fR
/* Always send EHLO at the start of a connection.
+/* .IP \fBsmtp_never_send_ehlo\fR
+/* Never send EHLO at the start of a connection.
/* .IP \fBsmtp_skip_4xx_greeting\fR
/* Skip servers that greet us with a 4xx status code.
/* .IP \fBsmtp_skip_5xx_greeting\fR
char *var_bestmx_transp;
char *var_error_rcpt;
int var_smtp_always_ehlo;
+int var_smtp_never_ehlo;
char *var_smtp_sasl_opts;
char *var_smtp_sasl_passwd;
bool var_smtp_sasl_enable;
smtp_chat_notify(state);
smtp_session_free(state->session);
debug_peer_restore();
-}
+ }
- /*
- * Clean up.
- */
-vstring_free(why);
-smtp_chat_reset(state);
-result = state->status;
-smtp_state_free(state);
+ /*
+ * Clean up.
+ */
+ vstring_free(why);
+ smtp_chat_reset(state);
+ result = state->status;
+ smtp_state_free(state);
-return (result);
+ return (result);
}
/* smtp_service - perform service for client */
VAR_IGN_MX_LOOKUP_ERR, DEF_IGN_MX_LOOKUP_ERR, &var_ign_mx_lookup_err,
VAR_SKIP_QUIT_RESP, DEF_SKIP_QUIT_RESP, &var_skip_quit_resp,
VAR_SMTP_ALWAYS_EHLO, DEF_SMTP_ALWAYS_EHLO, &var_smtp_always_ehlo,
+ VAR_SMTP_NEVER_EHLO, DEF_SMTP_NEVER_EHLO, &var_smtp_never_ehlo,
VAR_SMTP_SASL_ENABLE, DEF_SMTP_SASL_ENABLE, &var_smtp_sasl_enable,
0,
};
#include <mail_params.h>
#include <mail_addr.h>
#include <post_mail.h>
+#include <mail_error.h>
/* Application-specific. */
{
SMTP_SESSION *session = state->session;
static SMTP_RESP rdata;
- int more;
char *cp;
int last_char;
VSTRING_RESET(rdata.buf);
for (;;) {
last_char = smtp_get(state->buffer, session->stream, var_line_limit);
- cp = printable(STR(state->buffer), '?');
+ printable(STR(state->buffer), '?');
if (last_char != '\n')
msg_warn("%s: response longer than %d: %.30s...",
- session->namaddr, var_line_limit, cp);
+ session->namaddr, var_line_limit, STR(state->buffer));
if (msg_verbose)
- msg_info("< %s: %s", session->namaddr, cp);
- while (ISDIGIT(*cp))
- cp++;
- rdata.code = (cp - STR(state->buffer) == 3 ?
- atoi(STR(state->buffer)) : 0);
- more = (*cp == '-');
+ msg_info("< %s: %s", session->namaddr, STR(state->buffer));
/*
* Defend against a denial of service attack by limiting the amount
vstring_strcat(rdata.buf, STR(state->buffer));
smtp_chat_append(state, "In: ", STR(state->buffer));
}
- if (VSTRING_LEN(state->buffer) == 0) /* XXX remote brain damage */
- continue;
- if (!ISDIGIT(STR(state->buffer)[0])) /* XXX remote brain damage */
- continue;
- if (more == 0)
- break;
+
+ /*
+ * Parse into code and text. Ignore unrecognized garbage. This means
+ * that any character except space (or end of line) will have the
+ * same effect as the '-' line continuation character.
+ */
+ for (cp = STR(state->buffer); *cp && ISDIGIT(*cp); cp++)
+ /* void */ ;
+ if (cp - STR(state->buffer) == 3) {
+ if (*cp == '-')
+ continue;
+ if (*cp == ' ' || *cp == 0)
+ break;
+ }
+ state->error_mask |= MAIL_ERROR_PROTOCOL;
}
+ rdata.code = atoi(STR(state->buffer));
VSTRING_TERMINATE(rdata.buf);
rdata.str = STR(rdata.buf);
return (&rdata);
}
if (var_smtp_always_ehlo)
state->features |= SMTP_FEATURE_ESMTP;
+ if (var_smtp_never_ehlo)
+ state->features &= ~SMTP_FEATURE_ESMTP;
/*
* Return the compliment. Fall back to SMTP if our ESMTP recognition
/* int fd;
/* int lock_style;
/* int operation;
-/*
-/* int myflock_locked(err)
-/* int err;
/* DESCRIPTION
-/* myflock() locks or unlocks an entire open file. Depending
-/* on the value of the \fIlock_style\fR argument, this function uses
-/* either the fcntl() or the flock() system call.
+/* myflock() locks or unlocks an entire open file.
/*
/* In the case of a blocking request, a call that fails due to
-/* transient problems is tried again once per second.
-/* In the case of a non-blocking request, use the myflock_locked()
-/* call to distinguish between expected and unexpected failures.
-/*
-/* myflock_locked() examines the errno result from a failed
-/* non-blocking lock request, and returns non-zero (true)
-/* when the lock failed because someone else holds it.
+/* forseeable transient problems is retried once per second.
/*
/* Arguments:
/* .IP fd
/* One of the following values:
/* .RS
/* .IP MYFLOCK_STYLE_FLOCK
-/* Use BSD-style flock() locks.
+/* Use BSD-style flock() locking.
/* .IP MYFLOCK_STYLE_FCNTL
-/* Use POSIX-style fcntl() locks.
+/* Use POSIX-style fcntl() locking.
/* .RE
/* .IP operation
/* One of the following values:
/* .RS
/* .IP MYFLOCK_OP_NONE
-/* Releases any locks the process has on the specified open file.
+/* Release any locks the process has on the specified open file.
/* .IP MYFLOCK_OP_SHARED
-/* Attempts to acquire a shared lock on the specified open file.
+/* Attempt to acquire a shared lock on the specified open file.
/* This is appropriate for read-only access.
/* .IP MYFLOCK_OP_EXCLUSIVE
-/* Attempts to acquire an exclusive lock on the specified open
+/* Attempt to acquire an exclusive lock on the specified open
/* file. This is appropriate for write access.
/* .PP
/* In addition, setting the MYFLOCK_OP_NOWAIT bit causes the
/* call to return immediately when the requested lock cannot
-/* be acquired. See the myflock_locked() function on lock_style to deal
-/* with a negative result.
+/* be acquired.
/* .RE
/* DIAGNOSTICS
/* myflock() returns 0 in case of success, -1 in case of failure.
/* A problem description is returned via the global \fIerrno\fR
-/* variable.
+/* variable. In the case of a non-blocking lock request the value
+/* EAGAIN means that a lock is claimed by someone else.
/*
-/* Panic: attempts to use an unsupported file locking method.
-/* to use multiple locking methods, or none.
+/* Panic: attempts to use an unsupported file locking method or
+/* to implement an unsupported operation.
/* LICENSE
/* .ad
/* .fi
/* Utility library. */
#include "msg.h"
-#include "vstring.h"
#include "myflock.h"
/* myflock - lock/unlock entire open file */
int myflock(int fd, int lock_style, int operation)
{
+ int status;
+
+ /*
+ * Sanity check.
+ */
+ if ((operation & (MYFLOCK_OP_BITS)) != operation)
+ msg_panic("myflock: improper operation type: 0x%x", operation);
+
switch (lock_style) {
/*
-1, LOCK_SH | LOCK_NB, LOCK_EX | LOCK_NB, -1
};
- if ((operation & (MYFLOCK_OP_BITS)) != operation)
- msg_panic("myflock: improper operation type: 0x%x", operation);
- return (flock(fd, lock_ops[operation]));
+ status = flock(fd, lock_ops[operation]);
+ break;
}
#endif
static int lock_ops[] = {
F_UNLCK, F_RDLCK, F_WRLCK
};
- int ret;
- if ((operation & (MYFLOCK_OP_BITS)) != operation)
- msg_panic("myflock: improper operation type: 0x%x", operation);
memset((char *) &lock, 0, sizeof(lock));
lock.l_type = lock_ops[operation & ~MYFLOCK_OP_NOWAIT];
request = (operation & MYFLOCK_OP_NOWAIT) ? F_SETLK : F_SETLKW;
- while ((ret = fcntl(fd, request, &lock)) < 0
+ while ((status = fcntl(fd, request, &lock)) < 0
&& request == F_SETLKW
&& (errno == EINTR || errno == ENOLCK || errno == EDEADLK))
sleep(1);
- return (ret);
+ break;
}
#endif
default:
msg_panic("myflock: unsupported lock style: 0x%x", lock_style);
}
-}
-/* myflock_locked - were we locked out or what? */
+ /*
+ * Return a consistent result. Some systems return EACCES when a lock is
+ * taken by someone else, and that would complicate error processing.
+ */
+ if (status < 0 && (operation & MYFLOCK_OP_NOWAIT) != 0)
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EACCES)
+ errno = EAGAIN;
-int myflock_locked(int err)
-{
- return (err == EAGAIN || err == EWOULDBLOCK || err == EACCES);
+ return (status);
}
* External interface.
*/
extern int myflock(int, int, int);
-extern int myflock_locked(int);
/*
* Lock styles.
/*
* Lock request types.
*/
-#define MYFLOCK_OP_NONE 0
+#define MYFLOCK_OP_NONE 0
#define MYFLOCK_OP_SHARED 1
#define MYFLOCK_OP_EXCLUSIVE 2
#define MYFLOCK_OP_NOWAIT 4
+
#define MYFLOCK_OP_BITS \
(MYFLOCK_OP_SHARED | MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT)
return (0);
if (myflock(vstream_fileno(fp), INTERNAL_LOCK,
MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0) {
- vstring_sprintf(why, "file %s: unable to lock: %m", path);
+ vstring_sprintf(why, "unable to set exclusive lock: %m");
vstream_fclose(fp);
return (0);
}
* Open an existing file.
*/
if ((fp = vstream_fopen(path, flags & ~(O_CREAT | O_EXCL), 0)) == 0) {
- vstring_sprintf(why, "error opening file %s: %m", path);
+ vstring_sprintf(why, "cannot open existing file: %m");
return (0);
}
if (fstat_st == 0)
fstat_st = &local_statbuf;
if (fstat(vstream_fileno(fp), fstat_st) < 0) {
- msg_fatal("file %s: bad status after open: %m", path);
+ msg_fatal("bad open file status: %m");
} else if (fstat_st->st_nlink != 1) {
- vstring_sprintf(why, "file %s: should not have multiple links", path);
+ vstring_sprintf(why, "file has multiple hard links");
} else if (S_ISDIR(fstat_st->st_mode)) {
- vstring_sprintf(why, "file %s: should not be a directory", path);
+ vstring_sprintf(why, "file is a directory");
}
/*
#endif
|| fstat_st->st_nlink != lstat_st.st_nlink
|| fstat_st->st_mode != lstat_st.st_mode) {
- vstring_sprintf(why, "file %s: %s", path, S_ISLNK(lstat_st.st_mode) ?
- "should not be a symbolic link" : "status changed after opening");
+ vstring_sprintf(why, "%s", S_ISLNK(lstat_st.st_mode) ?
+ "file is a symbolic link" : "file status changed unexpectedly");
}
/*
* follow symbolic links.
*/
if ((fp = vstream_fopen(path, flags | (O_CREAT | O_EXCL), mode)) == 0) {
- vstring_sprintf(why, "file %s: cannot open: %m", path);
+ vstring_sprintf(why, "cannot create file exclusively: %m");
return (0);
}
if (CHANGE_OWNER(user, group)
&& fchown(vstream_fileno(fp), user, group) < 0) {
- vstring_sprintf(why, "file %s: cannot change ownership: %m", path);
+ vstring_sprintf(why, "cannot change file ownership: %m");
}
/*
* Optionally look up the file attributes.
*/
if (st != 0 && fstat(vstream_fileno(fp), st) < 0)
- msg_fatal("file %s: cannot get status after open: %m", path);
+ msg_fatal("bad open file status: %m");
/*
* We are almost there...