1 /* $NetBSD: postlock.c,v 1.3 2020/03/18 19:05:18 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* postlock 1 6 /* SUMMARY 7 /* lock mail folder and execute command 8 /* SYNOPSIS 9 /* .fi 10 /* \fBpostlock\fR [\fB-c \fIconfig_dir\fR] [\fB-l \fIlock_style\fR] 11 /* [\fB-v\fR] \fIfile command...\fR 12 /* DESCRIPTION 13 /* The \fBpostlock\fR(1) command locks \fIfile\fR for exclusive 14 /* access, and executes \fIcommand\fR. The locking method is 15 /* compatible with the Postfix UNIX-style local delivery agent. 16 /* 17 /* Options: 18 /* .IP "\fB-c \fIconfig_dir\fR" 19 /* Read the \fBmain.cf\fR configuration file in the named directory 20 /* instead of the default configuration directory. 21 /* .IP "\fB-l \fIlock_style\fR" 22 /* Override the locking method specified via the 23 /* \fBmailbox_delivery_lock\fR configuration parameter (see below). 24 /* .IP \fB-v\fR 25 /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR 26 /* options make the software increasingly verbose. 27 /* .PP 28 /* Arguments: 29 /* .IP \fIfile\fR 30 /* A mailbox file. The user should have read/write permission. 31 /* .IP \fIcommand...\fR 32 /* The command to execute while \fIfile\fR is locked for exclusive 33 /* access. The command is executed directly, i.e. without 34 /* interpretation by a shell command interpreter. 35 /* DIAGNOSTICS 36 /* The result status is 75 (EX_TEMPFAIL) when \fBpostlock\fR(1) 37 /* could not perform the requested operation. Otherwise, the 38 /* exit status is the exit status from the command. 39 /* BUGS 40 /* With remote file systems, the ability to acquire a lock does not 41 /* necessarily eliminate access conflicts. Avoid file access by 42 /* processes running on different machines. 43 /* ENVIRONMENT 44 /* .ad 45 /* .fi 46 /* .IP \fBMAIL_CONFIG\fR 47 /* Directory with Postfix configuration files. 48 /* .IP \fBMAIL_VERBOSE\fR 49 /* Enable verbose logging for debugging purposes. 50 /* CONFIGURATION PARAMETERS 51 /* .ad 52 /* .fi 53 /* The following \fBmain.cf\fR parameters are especially relevant to 54 /* this program. 55 /* The text below provides only a parameter summary. See 56 /* \fBpostconf\fR(5) for more details including examples. 57 /* LOCKING CONTROLS 58 /* .ad 59 /* .fi 60 /* .IP "\fBdeliver_lock_attempts (20)\fR" 61 /* The maximal number of attempts to acquire an exclusive lock on a 62 /* mailbox file or \fBbounce\fR(8) logfile. 63 /* .IP "\fBdeliver_lock_delay (1s)\fR" 64 /* The time between attempts to acquire an exclusive lock on a mailbox 65 /* file or \fBbounce\fR(8) logfile. 66 /* .IP "\fBstale_lock_time (500s)\fR" 67 /* The time after which a stale exclusive mailbox lockfile is removed. 68 /* .IP "\fBmailbox_delivery_lock (see 'postconf -d' output)\fR" 69 /* How to lock a UNIX-style \fBlocal\fR(8) mailbox before attempting delivery. 70 /* RESOURCE AND RATE CONTROLS 71 /* .ad 72 /* .fi 73 /* .IP "\fBfork_attempts (5)\fR" 74 /* The maximal number of attempts to fork() a child process. 75 /* .IP "\fBfork_delay (1s)\fR" 76 /* The delay between attempts to fork() a child process. 77 /* MISCELLANEOUS CONTROLS 78 /* .ad 79 /* .fi 80 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" 81 /* The default location of the Postfix main.cf and master.cf 82 /* configuration files. 83 /* .IP "\fBimport_environment (see 'postconf -d' output)\fR" 84 /* The list of environment parameters that a privileged Postfix 85 /* process will import from a non-Postfix parent process, or name=value 86 /* environment overrides. 87 /* SEE ALSO 88 /* postconf(5), configuration parameters 89 /* LICENSE 90 /* .ad 91 /* .fi 92 /* The Secure Mailer license must be distributed with this software. 93 /* AUTHOR(S) 94 /* Wietse Venema 95 /* IBM T.J. Watson Research 96 /* P.O. Box 704 97 /* Yorktown Heights, NY 10598, USA 98 /* 99 /* Wietse Venema 100 /* Google, Inc. 101 /* 111 8th Avenue 102 /* New York, NY 10011, USA 103 /*--*/ 104 105 /* System library. */ 106 107 #include <sys_defs.h> 108 #include <sys/stat.h> 109 #include <sys/wait.h> 110 #include <stdlib.h> 111 #include <unistd.h> 112 #include <fcntl.h> 113 #include <errno.h> 114 115 /* Utility library. */ 116 117 #include <msg.h> 118 #include <vstring.h> 119 #include <vstream.h> 120 #include <msg_vstream.h> 121 #include <iostuff.h> 122 #include <warn_stat.h> 123 #include <clean_env.h> 124 125 /* Global library. */ 126 127 #include <mail_params.h> 128 #include <mail_version.h> 129 #include <dot_lockfile.h> 130 #include <deliver_flock.h> 131 #include <mail_conf.h> 132 #include <sys_exits.h> 133 #include <mbox_conf.h> 134 #include <mbox_open.h> 135 #include <dsn_util.h> 136 #include <mail_parm_split.h> 137 138 /* Application-specific. */ 139 140 /* usage - explain */ 141 142 static NORETURN usage(char *myname) 143 { 144 msg_fatal("usage: %s [-c config_dir] [-l lock_style] [-v] folder command...", myname); 145 } 146 147 /* fatal_exit - all failures are deemed recoverable */ 148 149 static void fatal_exit(void) 150 { 151 exit(EX_TEMPFAIL); 152 } 153 154 MAIL_VERSION_STAMP_DECLARE; 155 156 /* main - go for it */ 157 158 int main(int argc, char **argv) 159 { 160 DSN_BUF *why; 161 char *folder; 162 char **command; 163 int ch; 164 int fd; 165 struct stat st; 166 int count; 167 WAIT_STATUS_T status; 168 pid_t pid; 169 int lock_mask; 170 char *lock_style = 0; 171 MBOX *mp; 172 ARGV *import_env; 173 174 /* 175 * Fingerprint executables and core dumps. 176 */ 177 MAIL_VERSION_STAMP_ALLOCATE; 178 179 /* 180 * Be consistent with file permissions. 181 */ 182 umask(022); 183 184 /* 185 * To minimize confusion, make sure that the standard file descriptors 186 * are open before opening anything else. XXX Work around for 44BSD where 187 * fstat can return EBADF on an open file descriptor. 188 */ 189 for (fd = 0; fd < 3; fd++) 190 if (fstat(fd, &st) == -1 191 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) 192 msg_fatal("open /dev/null: %m"); 193 194 /* 195 * Process environment options as early as we can. We are not set-uid, 196 * and we are supposed to be running in a controlled environment. 197 */ 198 if (getenv(CONF_ENV_VERB)) 199 msg_verbose = 1; 200 201 /* 202 * Set up logging and error handling. Intercept fatal exits so we can 203 * return a distinguished exit status. 204 */ 205 msg_vstream_init(argv[0], VSTREAM_ERR); 206 msg_cleanup(fatal_exit); 207 208 /* 209 * Parse JCL. 210 */ 211 while ((ch = GETOPT(argc, argv, "c:l:v")) > 0) { 212 switch (ch) { 213 default: 214 usage(argv[0]); 215 break; 216 case 'c': 217 if (setenv(CONF_ENV_PATH, optarg, 1) < 0) 218 msg_fatal("out of memory"); 219 break; 220 case 'l': 221 lock_style = optarg; 222 break; 223 case 'v': 224 msg_verbose++; 225 break; 226 } 227 } 228 if (optind + 2 > argc) 229 usage(argv[0]); 230 folder = argv[optind]; 231 command = argv + optind + 1; 232 233 /* 234 * Read the config file. The command line lock style can override the 235 * configured lock style. 236 */ 237 mail_conf_read(); 238 /* Enforce consistent operation of different Postfix parts. */ 239 import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ); 240 update_env(import_env->argv); 241 argv_free(import_env); 242 lock_mask = mbox_lock_mask(lock_style ? lock_style : 243 get_mail_conf_str(VAR_MAILBOX_LOCK, DEF_MAILBOX_LOCK, 1, 0)); 244 245 /* 246 * Lock the folder for exclusive access. Lose the lock upon exit. The 247 * command is not supposed to disappear into the background. 248 */ 249 why = dsb_create(); 250 if ((mp = mbox_open(folder, O_APPEND | O_WRONLY | O_CREAT, 251 S_IRUSR | S_IWUSR, (struct stat *) 0, 252 -1, -1, lock_mask, "5.2.0", why)) == 0) 253 msg_fatal("open file %s: %s", folder, vstring_str(why->reason)); 254 dsb_free(why); 255 256 /* 257 * Run the command. Remove the lock after completion. 258 */ 259 for (count = 1; (pid = fork()) == -1; count++) { 260 msg_warn("fork %s: %m", command[0]); 261 if (count >= var_fork_tries) { 262 mbox_release(mp); 263 exit(EX_TEMPFAIL); 264 } 265 sleep(var_fork_delay); 266 } 267 switch (pid) { 268 case 0: 269 (void) msg_cleanup((MSG_CLEANUP_FN) 0); 270 execvp(command[0], command); 271 msg_fatal("execvp %s: %m", command[0]); 272 default: 273 if (waitpid(pid, &status, 0) < 0) 274 msg_fatal("waitpid: %m"); 275 vstream_fclose(mp->fp); 276 mbox_release(mp); 277 exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1); 278 } 279 } 280