1 /* $Id$ */
2 /*
3 * Copyright (c) 1990-1996 Sam Leffler
4 * Copyright (c) 1991-1996 Silicon Graphics, Inc.
5 * HylaFAX is a trademark of Silicon Graphics
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and
8 * its documentation for any purpose is hereby granted without fee, provided
9 * that (i) the above copyright notices and this permission notice appear in
10 * all copies of the software and related documentation, and (ii) the names of
11 * Sam Leffler and Silicon Graphics may not be used in any advertising or
12 * publicity relating to the software without the specific, prior written
13 * permission of Sam Leffler and Silicon Graphics.
14 *
15 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
17 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
20 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
21 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24 * OF THIS SOFTWARE.
25 */
26 #include "faxApp.h"
27 #include "config.h"
28
29 #include <errno.h>
30 #include <pwd.h>
31 #include <limits.h>
32 #include <grp.h>
33 #include <unistd.h>
34 extern "C" {
35 #if HAS_LOCALE
36 #include <locale.h>
37 #endif
38 }
39
40 #include "Sys.h"
41
42 /*
43 * getopt Iterator Interface.
44 */
45 extern int opterr, optind;
46 extern char* optarg;
47
GetoptIter(int ac,char ** av,const fxStr & s)48 GetoptIter::GetoptIter(int ac, char** av, const fxStr& s) : opts(s)
49 {
50 argc = ac;
51 argv = av;
52 optind = 1;
53 opterr = 0;
54 c = Sys::getopt(argc, argv, opts);
55 }
~GetoptIter()56 GetoptIter::~GetoptIter() {}
57
operator ++()58 void GetoptIter::operator++() { c = Sys::getopt(argc, argv, opts); }
operator ++(int)59 void GetoptIter::operator++(int) { c = Sys::getopt(argc, argv, opts); }
optArg() const60 const char* GetoptIter::optArg() const { return optarg; }
getArg()61 const char* GetoptIter::getArg()
62 { return optind < argc ? argv[optind] : ""; }
nextArg()63 const char* GetoptIter::nextArg()
64 { return optind < argc ? argv[optind++] : ""; }
65
faxApp()66 faxApp::faxApp()
67 {
68 running = false;
69 faxqfifo = -1;
70 setLogFacility(LOG_FAX); // default
71 #ifdef LC_CTYPE
72 setlocale(LC_CTYPE, ""); // for <ctype.h> calls
73 #endif
74 #ifdef LC_TIME
75 setlocale(LC_TIME, ""); // for strftime calls
76 #endif
77 signal(SIGPIPE, fxSIGHANDLER(SIG_IGN)); // for FIFO writes
78 }
~faxApp()79 faxApp::~faxApp() {}
80
81 void
initialize(int,char **)82 faxApp::initialize(int, char**)
83 {
84 openFIFOs();
85 }
open(void)86 void faxApp::open(void) { running = true; }
87 void
close(void)88 faxApp::close(void)
89 {
90 running = false;
91 if (faxqfifo != -1)
92 Sys::close(faxqfifo);
93 }
94
95 fxStr faxApp::getopts;
setOpts(const char * s)96 void faxApp::setOpts(const char* s) { getopts = s; }
getOpts()97 const fxStr& faxApp::getOpts() { return getopts; }
98
99 void
fatal(const char * fmt...)100 faxApp::fatal(const char* fmt ...)
101 {
102 va_list ap;
103 va_start(ap, fmt);
104 vlogError(fmt, ap);
105 va_end(ap);
106 exit(-1);
107 }
108
109 /*
110 * FIFO-related support.
111 */
112 const fxStr faxApp::fifoName = FAX_FIFO;
113
114 /*
115 * Open the requisite FIFO special files.
116 */
117 void
openFIFOs(void)118 faxApp::openFIFOs(void)
119 {
120 }
121
122 void
closeFIFOs(void)123 faxApp::closeFIFOs(void)
124 {
125 }
126
127 /*
128 * Open the specified FIFO file.
129 */
130 int
openFIFO(const char * fifoName,int mode,bool okToExist)131 faxApp::openFIFO(const char* fifoName, int mode, bool okToExist)
132 {
133 if (Sys::mkfifo(fifoName, mode & 0777) < 0) {
134 if (errno != EEXIST || !okToExist)
135 faxApp::fatal("Could not create %s: %m.", fifoName);
136 }
137 int fd = Sys::open(fifoName, CONFIG_OPENFIFO|O_NDELAY, 0);
138 if (fd == -1)
139 faxApp::fatal("Could not open FIFO file %s: %m.", fifoName);
140 if (!Sys::isFIFOFile(fd))
141 faxApp::fatal("%s is not a FIFO special file", fifoName);
142 // open should set O_NDELAY, but just to be sure...
143 if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NDELAY) < 0)
144 logError("openFIFO %s: fcntl: %m", fifoName);
145 return (fd);
146 }
147
148 /*
149 * Respond to input on a FIFO file descriptor.
150 */
151 int
FIFOInput(int fd)152 faxApp::FIFOInput(int fd)
153 {
154 char buf[2048];
155 int n;
156 while ((n = Sys::read(fd, buf, sizeof (buf)-1)) > 0) {
157 buf[n] = '\0';
158 /*
159 * Break up '\0'-separated records and strip
160 * any trailing '\n' so that "echo mumble>FIFO"
161 * works (i.e. echo appends a '\n' character).
162 */
163 char* bp = &buf[0];
164 do {
165 char* cp = strchr(bp, '\0');
166 if (cp > bp) {
167 if (cp[-1] == '\n')
168 cp[-1] = '\0';
169 FIFOMessage(bp);
170 }
171 bp = cp+1;
172 } while (bp < &buf[n]);
173 }
174 #ifdef FIFOSELECTBUG
175 /*
176 * Solaris 2.x botch (and some versions of IRIX 5.x).
177 *
178 * A client close of an open FIFO causes an M_HANGUP to be
179 * sent and results in the receiver's file descriptor being
180 * marked ``hung up''. This in turn causes select to
181 * perpetually return true and if we're running as a realtime
182 * process, brings the system to a halt. The workaround for
183 * Solaris 2.1 was to do a parallel reopen of the appropriate
184 * FIFO so that the original descriptor is recycled. This
185 * apparently no longer works in Solaris 2.2 or later and we
186 * are forced to close and reopen both FIFO descriptors (noone
187 * appears capable of answering why this this is necessary and
188 * I personally don't care...)
189 */
190 closeFIFOs(); openFIFOs();
191 #endif
192 return (0);
193 }
194
195 /*
196 * Process a message received through a FIFO.
197 */
198 void
FIFOMessage(const char * cp)199 faxApp::FIFOMessage(const char* cp)
200 {
201 logError("Bad fifo message \"%s\"", cp);
202 }
203
204 /*
205 * Send a message to the central queuer process.
206 */
207 bool
vsendQueuer(const char * fmt,va_list ap)208 faxApp::vsendQueuer(const char* fmt, va_list ap)
209 {
210 if (faxqfifo == -1) {
211 #ifdef FIFOSELECTBUG
212 /*
213 * We try multiple times to open the appropriate FIFO
214 * file because the system has a kernel bug that forces
215 * the server to close+reopen the FIFO file descriptors
216 * for each message received on the FIFO (yech!).
217 */
218 int tries = 0;
219 do {
220 if (tries > 0)
221 sleep(1);
222 faxqfifo = Sys::open(fifoName, O_WRONLY|O_NDELAY);
223 } while (faxqfifo == -1 && errno == ENXIO && ++tries < 5);
224 #else
225 faxqfifo = Sys::open(fifoName, O_WRONLY|O_NDELAY);
226 #endif
227 if (faxqfifo == -1)
228 return (false);
229 /*
230 * Turn off O_NDELAY so that write will block if FIFO is full.
231 */
232 if (fcntl(faxqfifo, F_SETFL, fcntl(faxqfifo, F_GETFL, 0) &~ O_NDELAY) < 0)
233 logError("fcntl: %m");
234 }
235 fxStr msg = fxStr::vformat(fmt, ap);
236 u_int len = msg.length() + 1;
237 if (Sys::write(faxqfifo, (const char*)msg, len) != (ssize_t)len) {
238 if (errno == EBADF || errno == EPIPE) // reader expired
239 Sys::close(faxqfifo), faxqfifo = -1;
240 else
241 logError("FIFO write failed: %m");
242 return (false);
243 } else
244 return (true);
245 }
246
247 /*
248 * Send a message to the central queuer process.
249 */
250 bool
sendQueuer(const char * fmt...)251 faxApp::sendQueuer(const char* fmt ...)
252 {
253 va_list ap;
254 va_start(ap, fmt);
255 bool ok = vsendQueuer(fmt, ap);
256 va_end(ap);
257 return (ok);
258 }
259
260 /*
261 * Send a modem status message to the central queuer process.
262 */
263 bool
sendModemStatus(const char * devid,const char * fmt0...)264 faxApp::sendModemStatus(const char* devid, const char* fmt0 ...)
265 {
266 fxStr fmt = fxStr::format("+%s:%s", devid, fmt0);
267 va_list ap;
268 va_start(ap, fmt0);
269 bool ok = vsendQueuer(fmt, ap);
270 va_end(ap);
271 return (ok);
272 }
273
274 /*
275 * Send a job status message to the central queuer process.
276 */
277 bool
sendJobStatus(const char * jobid,const char * fmt0...)278 faxApp::sendJobStatus(const char* jobid, const char* fmt0 ...)
279 {
280 fxStr fmt = fxStr::format("*%s:%s", jobid, fmt0);
281 va_list ap;
282 va_start(ap, fmt0);
283 bool ok = vsendQueuer(fmt, ap);
284 va_end(ap);
285 return (ok);
286 }
287
288 /*
289 * Send a receive status message to the central queuer process.
290 */
291 bool
sendRecvStatus(const char * devid,const char * fmt0...)292 faxApp::sendRecvStatus(const char* devid, const char* fmt0 ...)
293 {
294 fxStr fmt = fxStr::format("@%s:%s", devid, fmt0);
295 va_list ap;
296 va_start(ap, fmt0);
297 bool ok = vsendQueuer(fmt, ap);
298 va_end(ap);
299 return (ok);
300 }
301
302 /*
303 * Miscellaneous stuff.
304 */
305
306 /*
307 * Convert an identifier to the pathname for the
308 * device (required by the UUCP lock code). This
309 * is done converting '_'s to '/'s and then prepending
310 * _PATH_DEV. This is required for SVR4 systems
311 * which have their devices in subdirectories!
312 */
313 fxStr
idToDev(const fxStr & id)314 faxApp::idToDev(const fxStr& id)
315 {
316 fxStr dev(id);
317 u_int l;
318 while ((l = dev.next(0, '_')) < dev.length())
319 dev[l] = '/';
320 if (dev[0] == '/') return (dev); // path + device
321 return (_PATH_DEV | dev);
322 }
323
324 fxStr
devToID(const fxStr & id)325 faxApp::devToID(const fxStr& id)
326 {
327 fxStr devID(id);
328 fxStr prefix(_PATH_DEV);
329 u_int l = prefix.length();
330 if (devID.length() > l && devID.head(l) == prefix)
331 devID.remove(0, l);
332 while ((l = devID.next(0, '/')) < devID.length())
333 devID[l] = '_';
334 return (devID);
335 }
336
337 /*
338 * Force the real uid+gid to be the same as
339 * the effective ones. Must temporarily
340 * make the effective uid root in order to
341 * do the real id manipulations.
342 */
343 void
setRealIDs(void)344 faxApp::setRealIDs(void)
345 {
346 uid_t euid = geteuid();
347 if (seteuid(0) < 0)
348 logError("seteuid(root): %m");
349 if (setgid(getegid()) < 0)
350 logError("setgid: %m");
351 if (setuid(euid) < 0)
352 logError("setuid: %m");
353 }
354
355 static void
detachIO(void)356 detachIO(void)
357 {
358 endpwent(); // XXX some systems hold descriptors
359 closelog(); // XXX in case syslog has descriptor
360 int fd = Sys::open(_PATH_DEVNULL, O_RDWR);
361 if (fd == -1)
362 printf("Could not open null device file %s.", _PATH_DEVNULL);
363 dup2(fd, STDIN_FILENO);
364 dup2(fd, STDOUT_FILENO);
365 dup2(fd, STDERR_FILENO);
366 for (fd = Sys::getOpenMax()-1; fd >= 0; fd--)
367 if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
368 (void) Sys::close(fd);
369 }
370
371 const fxStr faxApp::quote = " \"";
372 const fxStr faxApp::enquote = "\"";
373
374 /*
375 * Run the specified shell command. If changeIDs is
376 * true, we set the real uid+gid to the effective; this
377 * is so that programs like sendmail show an informative
378 * from address.
379 */
380 bool
runCmd(const char * cmd,bool changeIDs,IOHandler * waiter)381 faxApp::runCmd(const char* cmd, bool changeIDs, IOHandler* waiter)
382 {
383 pid_t pid = fork();
384 switch (pid) {
385 case 0:
386 if (changeIDs)
387 setRealIDs();
388 detachIO();
389 execl("/bin/sh", "sh", "-c", cmd, (char*) NULL);
390 sleep(1); // XXX give parent time
391 _exit(127);
392 case -1:
393 logError("Can not fork for \"%s\"", cmd);
394 return (false);
395 default:
396 if (waiter == NULL)
397 { int status = 0;
398 Sys::waitpid(pid, status);
399 if (status != 0) {
400 logError("Bad exit status %#o for \'%s\'", status, cmd);
401 return (false);
402 }
403 } else
404 {
405 Dispatcher::instance().startChild(pid, waiter);
406 }
407 return (true);
408 }
409 }
410
411 /*
412 * Setup server uid+gid. Normally the server is started up
413 * by root and then sets its effective uid+gid to that of
414 * the ``fax'' user (the gid is used by hfaxd and should be
415 * the uid of the fax user). This permits the server to
416 * switch to ``root'' whenever it's necessary (in order to
417 * gain access to a root-specific function such as starting
418 * a getty process). Alternatively the server may be run
419 * setuid ``fax'' with the real uid of ``root'' (in order to
420 * do privileged operations).
421 */
422 void
setupPermissions(void)423 faxApp::setupPermissions(void)
424 {
425 if (getuid() != 0)
426 faxApp::fatal("The fax server must run with real uid root.\n");
427 uid_t euid = geteuid();
428 const passwd* pwd = getpwnam(FAX_USER);
429 if (!pwd)
430 faxApp::fatal("No fax user \"%s\" defined on your system!\n"
431 "This software is not installed properly!", FAX_USER);
432 if (euid == 0) {
433 if (initgroups(pwd->pw_name, pwd->pw_gid) != 0)
434 faxApp::fatal("Can not setup permissions (supplementary groups)");
435 if (setegid(pwd->pw_gid) < 0)
436 faxApp::fatal("Can not setup permissions (gid)");
437 if (seteuid(pwd->pw_uid) < 0)
438 faxApp::fatal("Can not setup permissions (uid)");
439 } else {
440 uid_t faxuid = pwd->pw_uid;
441 setpwent();
442 pwd = getpwuid(euid);
443 if (!pwd)
444 faxApp::fatal("Can not figure out the identity of uid %u", euid);
445 if (pwd->pw_uid != faxuid)
446 faxApp::fatal("Configuration error; "
447 "the fax server must run as the fax user \"%s\".", FAX_USER);
448 (void) setegid(faxuid);
449 }
450 endpwent();
451 }
452
453 /*
454 * Break the association with the controlling tty if we can
455 * preserve it later with the POSIX O_NOCTTY mechanism. Note
456 * that we do not use detachIO to close all the open file
457 * descriptors because many systems cache open descriptors within
458 * libraries for performance reasons and do not react well when
459 * you close them w/o telling them about it (and some don't react
460 * well even when you *DO* tell them). Since we know we're called
461 * very early on from main in all our apps we just assume that
462 * we only need to remove the stdin+stdout+stderr before forking
463 * and starting a new session.
464 */
465 void
detachFromTTY(void)466 faxApp::detachFromTTY(void)
467 {
468 #ifdef O_NOCTTY
469 int fd = Sys::open(_PATH_DEVNULL, O_RDWR);
470 if (fd == -1)
471 printf("Could not open null device file %s.", _PATH_DEVNULL);
472 dup2(fd, STDIN_FILENO);
473 dup2(fd, STDOUT_FILENO);
474 dup2(fd, STDERR_FILENO);
475 switch (fork()) {
476 case 0: break; // child, continue
477 case -1: _exit(1); // error
478 default: _exit(0); // parent, terminate
479 }
480 (void) setsid();
481 #endif
482 }
483
484 /*
485 * Private version of fxassert for server processes.
486 * The default library routine sends the message to
487 * stderr and then calls abort(). This typically
488 * does not work for a server because stderr is not
489 * attached to anything (so the message is lost) and
490 * abort will not generate a core dump because the
491 * process has an effective uid and/or gid different
492 * from the real uid/gid.
493 *
494 * If (the undocumented configuration parameter)
495 * CONFIG_WAITONASSERT is set to a non-zero value
496 * then instead of dumping core the process will
497 * pause indefinitely so that a debugger can be
498 * attached.
499 */
500 extern "C" void
_fxassert(const char * msg,const char * file,int line)501 _fxassert(const char* msg, const char* file, int line)
502 {
503 fprintf(stderr, "Assertion failed \"%s\", file \"%s\" line %d.\n",
504 msg, file, line);
505 logError("Assertion failed \"%s\", file \"%s\" line %d.\n",
506 msg, file, line);
507 #if CONFIG_WAITONASSERT
508 for (;;) // wait for a debugger to attach
509 pause();
510 #else
511 faxApp::setRealIDs(); // reset so we get a core dump
512 abort();
513 #endif
514 /*NOTREACHED*/
515 }
516