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