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 <sys/types.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <pwd.h>
30 #include <math.h>
31 #include <limits.h>
32 #include <sys/file.h>
33 #include <grp.h>
34 #include <unistd.h>
35 
36 #include "Sys.h"
37 
38 #include "Dispatcher.h"
39 
40 #include "FaxRecvInfo.h"
41 #include "FaxMachineInfo.h"
42 #include "FaxAcctInfo.h"
43 #include "faxGettyApp.h"
44 #include "UUCPLock.h"
45 #include "Getty.h"
46 #include "REArray.h"
47 #include "BoolArray.h"
48 #include "config.h"
49 
50 /*
51  * HylaFAX Spooling and Command Agent.
52  */
AnswerTimeoutHandler()53 AnswerTimeoutHandler::AnswerTimeoutHandler() {}
~AnswerTimeoutHandler()54 AnswerTimeoutHandler::~AnswerTimeoutHandler() {}
timerExpired(long,long)55 void AnswerTimeoutHandler::timerExpired(long, long)
56     { faxGettyApp::instance().answerCleanup(); }
57 
58 const fxStr faxGettyApp::recvDir	= FAX_RECVDIR;
59 
60 faxGettyApp* faxGettyApp::_instance = NULL;
61 
faxGettyApp(const fxStr & devName,const fxStr & devID)62 faxGettyApp::faxGettyApp(const fxStr& devName, const fxStr& devID)
63     : FaxServer(devName, devID)
64 {
65     devfifo = -1;
66     modemLock = NULL;
67     setupConfig();
68 
69     fxAssert(_instance == NULL, "Cannot create multiple faxGettyApp instances");
70     _instance = this;
71 }
72 
~faxGettyApp()73 faxGettyApp::~faxGettyApp()
74 {
75     delete modemLock;
76 }
77 
instance()78 faxGettyApp& faxGettyApp::instance() { return *_instance; }
79 
80 void
initialize(int argc,char ** argv)81 faxGettyApp::initialize(int argc, char** argv)
82 {
83     FaxServer::initialize(argc, argv);
84     faxApp::initialize(argc, argv);
85 
86     for (GetoptIter iter(argc, argv, getOpts()); iter.notDone(); iter++)
87 	switch (iter.option()) {
88 	case 'c':			// set configuration parameter
89 	    readConfigItem(iter.optArg());
90 	    break;
91 	case 'D':			// detach process from tty
92 	    detachFromTTY();
93 	    break;
94 	}
95     modemLock = getUUCPLock(getModemDevice());
96     /*
97      * Notify queuer that modem exists and what the
98      * phone number is.  This is used by the queuer
99      * when processing other messages we sent
100      * later--such as when a document is received.
101      */
102     sendModemStatus("N" | getModemNumber());
103 }
104 
105 void
open()106 faxGettyApp::open()
107 {
108     traceServer("OPEN %s  %s"
109         , (const char*) getModemDevice()
110         , HYLAFAX_VERSION
111     );
112     faxApp::open();
113     FaxServer::open();
114 }
115 
116 void
close()117 faxGettyApp::close()
118 {
119     if (isRunning()) {
120 	sendModemStatus("D");
121 	traceServer("CLOSE " | getModemDevice());
122 	faxApp::close();
123 	FaxServer::close();
124     }
125 }
126 
lockModem()127 bool faxGettyApp::lockModem()		{ return modemLock->lock(); }
unlockModem()128 void faxGettyApp::unlockModem()		{ modemLock->unlock(); }
canLockModem()129 bool faxGettyApp::canLockModem()	{ return modemLock->check(); }
isModemLocked()130 bool faxGettyApp::isModemLocked()	{ return modemLock->isLocked(); }
131 
132 bool
setupModem(bool isSend)133 faxGettyApp::setupModem(bool isSend)
134 {
135     /*
136      * Reread the configuration file if it has been
137      * changed.  We do this before each setup operation
138      * since we are a long-running process and it should
139      * not be necessary to restart the process to have
140      * config file changes take effect.
141      */
142     if (updateConfig(getConfigFile()))
143 	sendModemStatus("N" | getModemNumber());
144     if (FaxServer::setupModem(false) && ModemServer::readyModem()) {
145 	/*
146 	 * Setup modem for receiving.
147 	 */
148 	FaxModem* modem = (FaxModem*) ModemServer::getModem();
149 	modem->setupReceive();
150 	/*
151 	 * If the server is configured, listen for incoming calls.
152 	 */
153 	setRingsBeforeAnswer(ringsBeforeAnswer);
154 	return (true);
155     } else
156 	return (false);
157 }
158 
159 /*
160  * Discard any handle on the modem.
161  */
162 void
discardModem(bool dropDTR)163 faxGettyApp::discardModem(bool dropDTR)
164 {
165     int fd = getModemFd();
166     if (fd >= 0) {
167 	if (Dispatcher::instance().handler(fd, Dispatcher::ReadMask))
168 	    Dispatcher::instance().unlink(fd);
169     }
170     FaxServer::discardModem(dropDTR);
171 }
172 
173 /*
174  * Begin listening for ring status messages from the modem.
175  * The modem is locked and we mark it as busy to the queuer
176  * (just as if we were answering a call).  We listen for one
177  * ring status message and, if successful, keep the modem
178  * locked and change state so that further input will be
179  * consumed.
180  */
181 void
listenBegin()182 faxGettyApp::listenBegin()
183 {
184     if (lockModem()) {
185 	changeState(LISTENING);
186 	sendModemStatus("B");
187 	ringsHeard = 0;
188 	listenForRing();
189     } else if (state != SENDING && state != ANSWERING && state != RECEIVING) {
190 	/*
191 	 * The modem is in use to call out, or some other process
192 	 * has grabbed the lock to handle the incoming call.
193 	 * Discard our handle on the modem and change to LOCKWAIT
194 	 * state where we wait for the modem to come available again.
195 	 */
196 	traceServer("ANSWER: Can not lock modem device");
197 	discardModem(false);
198 	changeState(LOCKWAIT, pollLockWait);
199 	sendModemStatus("U");
200     }
201 }
202 
203 /*
204  * Consume a ring status message from the modem.  We assume
205  * the modem is locked and that input is present.  If we don't
206  * see a ring then we exit LISTENING state and reset our
207  * handle on the modem.  If the number of rings received
208  * matches ringsBeforeAnswer then we answer the phone.
209  */
210 void
listenForRing()211 faxGettyApp::listenForRing()
212 {
213     Dispatcher::instance().stopTimer(&answerHandler);
214 
215     bool again;
216 
217     do {
218         CallType ctype = ClassModem::CALLTYPE_UNKNOWN;
219 	CallID callid(idConfig.length());
220         again = false;
221 
222         if (modemWaitForRings(ringsHeard, ctype, callid)) {
223 	    if (! callid.isEmpty()) {
224 		received_callid = callid;	// CNID is only sent once.  Store it
225 						// for answering after later RINGs.
226 		fxStr send;
227 		for (u_int i = 0; i < received_callid.size(); i++) {
228 		    if (i) send.append(',');
229 		    send.append("\"" | received_callid[i] | "\"");
230 		    traceServer("ANSWER: Call ID %d \"%s\"", i+1, received_callid.id(i));
231 		}
232 		faxApp::sendModemStatus(getModemDeviceID(), "C%s", (const char*) send);
233 	    }
234 	    ++ringsHeard;
235             if (dringOn.length() && ctype == ClassModem::CALLTYPE_UNKNOWN) --ringsHeard;
236              // distinctive ring failed to identify - get another ring
237 
238 	    /* DID modems may only signal a call with DID data - no RING */
239 	    bool done = false;
240 	    for (u_int i = 0; i < callid.size(); i++) {
241 		if ((u_int) idConfig[i].answerlength > 0 &&
242 		    callid[i].length() >= (u_int) idConfig[i].answerlength) {
243 		    done = true;
244 		    break;
245 		}
246 	    }
247 	    if (done || ringsBeforeAnswer && (ringsHeard >= ringsBeforeAnswer))
248 		answerPhone(ClassModem::ANSTYPE_ANY, ctype, received_callid);
249 	    else if (isModemInput())
250 	        again = true;
251 	    else
252 	        // NB: 10 second timeout should be plenty
253 	        Dispatcher::instance().startTimer(10, 0, &answerHandler);
254         } else
255 	    answerCleanup();
256     } while (again);
257 }
258 
259 /*
260  * Handle a user request to answer the phone.
261  * Note that this can come in when we are in
262  * many different states.
263  */
264 void
answerPhoneCmd(AnswerType atype,const char * dialnumber)265 faxGettyApp::answerPhoneCmd(AnswerType atype, const char* dialnumber)
266 {
267     CallType ctype = ClassModem::CALLTYPE_UNKNOWN;
268     if (state == LISTENING) {
269 	/*
270 	 * We were listening to rings when an answer command
271 	 * was received.  The modem is already locked so just
272 	 * cancel any timeout and answer the call.
273 	 */
274 	Dispatcher::instance().stopTimer(&answerHandler);
275 	answerPhone(atype, ctype, received_callid, dialnumber);
276     } else if (lockModem()) {
277 	/*
278 	 * The modem is ours, notifier the queuer and answer.
279 	 */
280 	sendModemStatus("B");
281 	answerPhone(atype, ctype, received_callid, dialnumber);
282     } else if (state != ANSWERING && state != RECEIVING) {
283 	/*
284 	 * The modem is in use to call out, or some other process
285 	 * has grabbed the lock to handle the incoming call.
286 	 * Discard our handle on the modem and change to LOCKWAIT
287 	 * state where we wait for the modem to come available again.
288 	 */
289 	discardModem(false);
290 	changeState(LOCKWAIT, pollLockWait);
291 	sendModemStatus("U");
292     }
293 }
294 
295 /*
296  * Answer the telephone in response to data from the modem
297  * (e.g. a "RING" message) or an explicit command from the
298  * user (sending an "ANSWER" command through the FIFO).
299  */
300 void
answerPhone(AnswerType atype,CallType ctype,const CallID & callid,const char * dialnumber)301 faxGettyApp::answerPhone(AnswerType atype, CallType ctype, const CallID& callid, const char* dialnumber)
302 {
303     changeState(ANSWERING);
304     beginSession(FAXNumber);
305     sendModemStatus("I" | getCommID());
306 
307     FaxAcctInfo ai;
308     ai.user = "fax";
309     ai.commid = getCommID();
310     ai.start = Sys::now();
311     ai.device = getModemDeviceID();
312     ai.dest = getModemNumber();
313     ai.callid = callid;
314     ai.npages = 0;
315     ai.params = 0;
316     ai.csi = "";
317     ai.jobid = "";
318     ai.jobtag = "";
319     ai.owner = "";
320     /*
321      * Answer the phone according to atype.  If this is
322      * ``any'', then pick a more specific type.  If
323      * adaptive-answer is enabled and ``any'' is requested,
324      * then rotate through the set of possibilities.
325      */
326     bool callResolved;
327     bool advanceRotary = true;
328     Status eresult;
329 
330     fxStr callid_formatted = "";
331 
332     /*
333      * We default to accepting all calls.
334      * If the DynamicConfig set's RejectCall to true, then we
335      * will reject it.
336      */
337     rejectCall = false;
338 
339     for (u_int i = 0; i < callid.size(); i++)
340 	callid_formatted.append(quote | callid.id(i) | enquote);
341 
342     if (callid_formatted.length())
343     	traceProtocol("CallID:%s", (const char*) callid_formatted);
344 
345     if (dynamicConfig.length()) {
346 	fxStr cmd(dynamicConfig | quote | getModemDevice() | enquote | callid_formatted);
347 	traceServer("DynamicConfig: %s", (const char*)cmd);
348 	fxStr localid = "";
349 	int pipefd[2], status;
350 	char line[1024];
351 	pipe(pipefd);
352 	pid_t pid = fork();
353 	switch (pid) {
354 	    case -1:
355 		eresult = Status(305, "Could not fork for local ID");
356 		logError("%s", eresult.string());
357 		Sys::close(pipefd[0]);
358 		Sys::close(pipefd[1]);
359 		break;
360 	    case  0:
361 		dup2(pipefd[1], STDOUT_FILENO);
362 		Sys::close(pipefd[0]);
363 		Sys::close(pipefd[1]);
364 		execl("/bin/sh", "sh", "-c", (const char*) cmd, (char*) NULL);
365 		sleep(1);
366 		_exit(1);
367 	    default:
368 		Sys::close(pipefd[1]);
369 		{
370 		    FILE* fd = fdopen(pipefd[0], "r");
371 		    while (fgets(line, sizeof (line)-1, fd)){
372 			line[strlen(line)-1]='\0';		// Nuke \n at end of line
373 			(void) readConfigItem(line);
374 		    }
375 		    Sys::waitpid(pid, status);
376 		    if (status != 0)
377 		    {
378 			eresult = Status(306, "Bad exit status %#o for \'%s\'", status, (const char*) cmd);
379 			logError("%s", eresult.string());
380 		    }
381 		    // modem settings may have changed...
382 		    FaxModem* modem = (FaxModem*) ModemServer::getModem();
383 		    modem->pokeConfig(false);
384 		}
385 		Sys::close(pipefd[0]);
386 		break;
387 	}
388     }
389 
390     if (rejectCall)
391     {
392 	/*
393 	 * Call was rejected based on Caller ID information.
394 	 */
395 	eresult = Status(308, "ANSWER: CALL REJECTED");
396 	traceServer("%s", eresult.string());
397 	callResolved = false;
398 	advanceRotary = false;
399     } else {
400 	if (ctype != ClassModem::CALLTYPE_UNKNOWN) {
401 	    /*
402 	     * Distinctive ring or other means has already identified
403 	     * the type of call.  If we're to answer the call in a
404 	     * different way, then treat this as an error and don't
405 	     * answer the phone.  Otherwise answer according to the
406 	     * deduced call type.
407 	     */
408 	    if (atype != ClassModem::ANSTYPE_ANY && ctype != atype) {
409 	        eresult = Status(309, "ANSWER: Call deduced as %s,"
410 		     " but told to answer as %s; call ignored",
411 		     ClassModem::callTypes[ctype],
412 		     ClassModem::answerTypes[atype]);
413 		traceServer("%s", eresult.string());
414 		callResolved = false;
415 		advanceRotary = false;
416 	    } else {
417 		// NB: answer based on ctype, not atype
418 		if (!(noAnswerVoice && ctype == ClassModem::CALLTYPE_VOICE))
419 		    ctype = modemAnswerCall(ctype, eresult, dialnumber);
420 		callResolved = processCall(ctype, eresult, callid);
421 	    }
422 	} else if (atype == ClassModem::ANSTYPE_ANY) {
423 	    /*
424 	     * Normal operation; answer according to the settings
425 	     * for the rotary and adaptive answer capabilities.
426 	     */
427 	    int r = answerRotor;
428 	    do {
429 		callResolved = answerCall(answerRotary[r], ctype, eresult, callid, dialnumber);
430 		r = (r+1) % answerRotorSize;
431 	    } while (!callResolved && adaptiveAnswer && r != answerRotor);
432 	} else {
433 	    /*
434 	     * Answer for a specific type of call but w/o
435 	     * any existing call type information such as
436 	     * distinctive ring.
437 	     */
438 	    callResolved = answerCall(atype, ctype, eresult, callid, dialnumber);
439 	}
440     }
441     if (eresult.value() != 0)
442 	traceProtocol("%s", eresult.string());
443     /*
444      * Call resolved.  If we were able to recognize the call
445      * type and setup a session, then reset the answer rotary
446      * state if there is a bias toward a specific answer type.
447      * Otherwise, if the call failed, advance the rotor to
448      * the next answer type in preparation for the next call.
449      */
450     if (callResolved) {
451 	if (answerBias != (u_int) -1)
452 	    answerRotor = answerBias;
453     } else if (advanceRotary) {
454 	if (adaptiveAnswer)
455 	    answerRotor = 0;
456 	else
457 	    answerRotor = (answerRotor+1) % answerRotorSize;
458     }
459     sendModemStatus("I");
460     endSession();
461 
462     ai.status = eresult.string();
463     ai.duration = Sys::now() - ai.start;
464     ai.conntime = ai.duration;
465     if (logCalls && !ai.record("CALL"))
466 	logError("Error writing CALL accounting record, dest=%s",
467 	    (const char*) ai.dest);
468 
469 
470     answerCleanup();
471 }
472 
473 /*
474  * Cleanup after answering a call or listening for ring status
475  * messages from the modem (when ringsBeforeAnswer is zero).
476  *
477  * If we still have a handle on the modem, then force a hangup
478  * and discard the handle.  We do this explicitly because some
479  * modems are impossible to safely hangup in the event of a
480  * problem.  Forcing a close on the device so that the modem
481  * will see DTR drop (hopefully) should clean up any bad state
482  * its in.  We then immediately try to setup the modem again
483  * so that we can be ready to answer incoming calls again.
484  *
485  * NB: the modem may have been discarded if a child process
486  *     was invoked to handle the inbound call.
487  */
488 void
answerCleanup()489 faxGettyApp::answerCleanup()
490 {
491     if (modemReady()) {
492 	modemHangup();
493 	discardModem(true);
494     }
495 
496     for (u_int i = 0; i < received_callid.size(); i++)
497 	received_callid[i].resize(0);
498 
499     bool isSetup;
500     if (isModemLocked() || lockModem()) {
501 	isSetup = setupModem(false);
502 	unlockModem();
503     } else
504 	isSetup = false;
505     if (isSetup)
506 	changeState(RUNNING, pollLockWait);
507     else
508 	changeState(MODEMWAIT, pollModemWait);
509 }
510 
511 /*
512  * Answer a call according to the specified answer type
513  * and return the deduced call type.  If we are to use an
514  * external application to deduce and possibly handle the
515  * call then we do it here and return the call type it
516  * comes up with.  Otherwise we use whatever deduction
517  * the modem layer arrives at as the call type.
518  */
519 bool
answerCall(AnswerType atype,CallType & ctype,Status & eresult,const CallID & callid,const char * dialnumber)520 faxGettyApp::answerCall(AnswerType atype, CallType& ctype, Status& eresult, const CallID& callid, const char* dialnumber)
521 {
522     bool callResolved;
523     if (atype == ClassModem::ANSTYPE_EXTERN) {
524 	if (egettyArgs != "") {
525 	    /*
526 	     * Use an external application to deduce and possibly
527 	     * handle the call.  We invoke the egetty application
528 	     * and interpret the exit status as the deduced call type.
529 	     * If the call has not been handled in the subprocess
530 	     * then we take action based on the returned call type.
531 	     */
532 	    ctype = runGetty("EXTERN GETTY", OSnewEGetty,
533 		egettyArgs, eresult, lockExternCalls, callid, true);
534 	    if (ctype == ClassModem::CALLTYPE_DONE)	// NB: call completed
535 		return (true);
536 	    if (ctype != ClassModem::CALLTYPE_ERROR)
537 		modemAnswerCallCmd(ctype);
538 	} else
539 	    eresult = Status(310, "External getty use is not permitted");
540     } else
541 	ctype = modemAnswerCall(atype, eresult, dialnumber);
542     callResolved = processCall(ctype, eresult, callid);
543     return (callResolved);
544 }
545 
546 /*
547  * Process an inbound call after the phone's been answered.
548  * Calls may either be handled within the process or through
549  * an external application.  Fax calls are handled internally.
550  * Other types of calls are handled with external apps.  The
551  * modem is conditioned for service, the process is started
552  * with the open file descriptor passed on stdin+stdout+stderr,
553  * and the local handle on the modem is discarded so that SIGHUP
554  * is delivered to the subprocess (group) on last close.  This
555  * process waits for the subprocess to terminate, at which time
556  * it removes the modem lock file and let's faxgetty continue on
557  * to recondition the modem for incoming calls (if configured).
558  */
559 bool
processCall(CallType ctype,Status & eresult,const CallID & callid)560 faxGettyApp::processCall(CallType ctype, Status& eresult, const CallID& callid)
561 {
562     bool callHandled = false;
563 
564     /*
565      * First of - turn of Dispatcher
566      */
567     int fd = getModemFd();
568     if (fd >= 0) {
569 	if (Dispatcher::instance().handler(fd, Dispatcher::ReadMask))
570 	    Dispatcher::instance().unlink(fd);
571     }
572     switch (ctype) {
573     case ClassModem::CALLTYPE_FAX:
574 	traceServer("ANSWER: FAX CONNECTION  DEVICE '%s'"
575 	    , (const char*) getModemDevice()
576 	);
577 	changeState(RECEIVING);
578 	sendRecvStatus(getModemDeviceID(), "B");
579 	callHandled = recvFax(callid, eresult);
580 	sendRecvStatus(getModemDeviceID(), "E");
581 	break;
582     case ClassModem::CALLTYPE_DATA:
583 	traceServer("ANSWER: DATA CONNECTION");
584 	if (gettyArgs != "") {
585 	    sendModemStatus("d");
586 	    runGetty("GETTY", OSnewGetty, gettyArgs, eresult, lockDataCalls, callid);
587 	    sendModemStatus("e");
588 	} else
589 	    traceServer("ANSWER: Data connections are not permitted");
590 	callHandled = true;
591 	break;
592     case ClassModem::CALLTYPE_VOICE:
593 	traceServer("ANSWER: VOICE CONNECTION");
594 	if (vgettyArgs != "") {
595 	    sendModemStatus("v");
596 	    runGetty("VGETTY", OSnewVGetty, vgettyArgs, eresult, lockVoiceCalls, callid);
597 	    sendModemStatus("w");
598 	} else
599 	    traceServer("ANSWER: Voice connections are not permitted");
600 	callHandled = true;
601 	break;
602     case ClassModem::CALLTYPE_ERROR:
603 	traceServer("ANSWER: %s", eresult.string());
604 	break;
605     }
606     return (callHandled);
607 }
608 
609 /*
610  * Run a getty subprocess and wait for it to terminate.
611  * The speed parameter is passed to use in establishing
612  * a login session.
613  */
614 CallType
runGetty(const char * what,Getty * (* newgetty)(const fxStr &,const fxStr &),const char * args,Status & eresult,bool keepLock,const CallID & callid,bool keepModem)615 faxGettyApp::runGetty(
616     const char* what,
617     Getty* (*newgetty)(const fxStr&, const fxStr&),
618     const char* args,
619     Status& eresult,
620     bool keepLock,
621     const CallID& callid,
622     bool keepModem
623 )
624 {
625     fxStr prefix(_PATH_DEV);
626     fxStr dev(getModemDevice());
627     if (dev.head(prefix.length()) == prefix)
628 	dev.remove(0, prefix.length());
629     Getty* getty = (*newgetty)(dev, fxStr::format("%u", getModemRate()));
630     if (getty == NULL) {
631 	eresult = Status(311, "%s: could not create", what);
632 	return (ClassModem::CALLTYPE_ERROR);
633     }
634 
635     getty->setupArgv(args, callid);
636 
637     /*
638      * The getty process should not inherit the lock file.
639      * Remove it here before the fork so that our state is
640      * correct (so further unlock calls will do nothing).
641      * Note that we remove the lock here because apps such
642      * as ppp and slip that install their own lock cannot
643      * cope with finding a lock in place (even if it has
644      * their pid in it).  This creates a potential window
645      * during which outbound jobs might grab the modem
646      * since they won't find a lock file in place.
647      */
648     if (!keepLock)
649 	unlockModem();
650     bool parentIsInit = (getppid() == 1);
651     pid_t pid = fork();
652     if (pid == -1) {
653 	eresult = Status(312, "%s: can not fork: %s", what, strerror(errno));
654 	delete getty;
655 	return (ClassModem::CALLTYPE_ERROR);
656     }
657     if (pid == 0) {			// child, start getty session
658 	setProcessPriority(BASE);	// remove any high priority
659 	if (keepLock)
660 	    /*
661 	     * The getty process should inherit the lock file.
662 	     * Force the UUCP lock owner so that apps find their
663 	     * own pid in the lock file.  Otherwise they abort
664 	     * thinking some other process already has control
665 	     * of the modem.  Note that doing this creates a
666 	     * potential window for stale lock removal between
667 	     * the time the login process terminates and the
668 	     * parent retakes ownership of the lock file (see below).
669 	     */
670 	    modemLock->setOwner(getpid());
671 	{
672 	  setpwent();
673 
674 	  uid_t uid = getuid();
675 	  gid_t gid = getgid();
676           uid_t euid = geteuid();
677           gid_t egid = getegid();
678 	  if (setegid(gid) < 0)
679 	      traceServer("runGetty::setegid: %m");
680 	  if (seteuid(uid) < 0)
681 	      traceServer("runGetty::seteuid (child): %m");
682           const struct passwd *pwd = getpwuid(euid);
683           if (initgroups(pwd->pw_name, egid) != 0)
684               traceServer("runGetty::initgroups: %m");
685 
686 	  endpwent();
687 	}
688 	getty->run(getModemFd(), parentIsInit);
689 	_exit(127);
690 	/*NOTREACHED*/
691     }
692     traceServer("%s: START \"%s\", pid %lu", what,
693 	(const char*) getty->getCmdLine(), (u_long) pid);
694     getty->setPID(pid);
695     /*
696      * Purge existing modem state because the getty+login
697      * processe will change everything and because we must
698      * close the descriptor so that the getty will get
699      * SIGHUP on last close.
700      */
701     if (!keepModem)
702 	discardModem(false);
703     changeState(GETTYWAIT);
704     int status;
705     getty->wait(status, true);		// wait for getty/login work to complete
706     if ( status > 1280 ) { // codes returned larger than 1280 are undefined and must be an error
707         status = 1024;
708         eresult = Status(313, "ERROR: Unknown status");
709     }
710     /*
711      * Retake ownership of the modem.  Note that there's
712      * a race in here (another process could come along
713      * and purge the lock file thinking it was stale because
714      * the pid is for the process that just terminated);
715      * the only way to avoid it is to use a real locking
716      * mechanism (e.g. flock on the lock file).
717      */
718     if (keepLock)
719 	modemLock->setOwner(0);		// NB: 0 =>'s use setup pid
720     uid_t euid = geteuid();
721     if (seteuid(getuid()) < 0)		// Getty::hangup assumes euid is root
722 	 traceServer("runGetty: seteuid (parent): %m");
723     getty->hangup();			// cleanup getty-related stuff
724     seteuid(euid);
725     traceServer("%s: exit status %#o", what, status);
726     delete getty;
727     return (CallType)((status >> 8) & 0xff);
728 }
729 
730 /*
731  * Set the number of rings to wait before answering
732  * the telephone.  If there is a modem setup, then
733  * configure the dispatcher to reflect whether or not
734  * we need to listen for data from the modem (the RING
735  * status messages).
736  */
737 void
setRingsBeforeAnswer(int rings)738 faxGettyApp::setRingsBeforeAnswer(int rings)
739 {
740     ringsBeforeAnswer = rings;
741     if (modemReady()) {
742 	int fd = getModemFd();
743 	Dispatcher::instance().link(fd, Dispatcher::ReadMask, this);
744 	/*
745 	 * Systems that have a tty driver based on SVR4 streams
746 	 * frequently don't implement select properly in that if
747 	 * output is collected by another process (e.g. cu, tip,
748 	 * kermit) quickly we do not reliably get woken up.  On
749 	 * these systems however the close on the tty usually
750 	 * causes us to wakeup from select for an exceptional
751 	 * condition on the descriptor--and this is good enough
752 	 * for us to do the work we need.
753 	 */
754 	Dispatcher::instance().link(fd, Dispatcher::ExceptMask, this);
755     }
756 }
757 
758 /*
759  * Notification handlers.
760  */
761 
762 /*
763  * Handle notification that the modem device has become
764  * available again after a period of being unavailable.
765  */
766 void
notifyModemReady()767 faxGettyApp::notifyModemReady()
768 {
769     (void) faxApp::sendModemStatus(getModemDeviceID(),
770 	readyState | getModemCapabilities() | ":%02x", modemPriority);
771 }
772 
773 /*
774  * Handle notification that the modem device looks to
775  * be in a state that requires operator intervention.
776  */
777 void
notifyModemWedged()778 faxGettyApp::notifyModemWedged()
779 {
780     if (!sendModemStatus("W"))
781 	logError("MODEM %s appears to be wedged",
782 	    (const char*) getModemDevice());
783     close();
784 }
785 
786 void
notifyRecvBegun(FaxRecvInfo & ri)787 faxGettyApp::notifyRecvBegun(FaxRecvInfo& ri)
788 {
789     (void) sendRecvStatus(getModemDeviceID(), "S%s", (const char*) ri.encode());
790 
791     FaxServer::notifyRecvBegun(ri);
792 }
793 
794 /*
795  * Handle notification that a page has been received.
796  */
797 void
notifyPageRecvd(TIFF * tif,FaxRecvInfo & ri,int ppm)798 faxGettyApp::notifyPageRecvd(TIFF* tif, FaxRecvInfo& ri, int ppm)
799 {
800     (void) sendRecvStatus(getModemDeviceID(), "P%s", (const char*) ri.encode());
801 
802     FaxServer::notifyPageRecvd(tif, ri, ppm);
803 
804     // XXX fill in
805 }
806 
807 /*
808  * Handle notification that a document has been received.
809  */
810 void
notifyDocumentRecvd(FaxRecvInfo & ri)811 faxGettyApp::notifyDocumentRecvd(FaxRecvInfo& ri)
812 {
813     (void) sendRecvStatus(getModemDeviceID(), "D%s", (const char*) ri.encode());
814 
815     FaxServer::notifyDocumentRecvd(ri);
816 
817     FaxAcctInfo ai;
818     ai.user = "fax";
819     ai.commid = getCommID();
820     ai.duration = (time_t) ri.time;
821     ai.start = Sys::now() - ai.duration;
822     ai.conntime = ai.duration;
823     ai.device = getModemDeviceID();
824     ai.dest = getModemNumber();
825     ai.csi = ri.sender;
826     ai.npages = ri.npages;
827     ai.params = ri.params.encode();
828     ai.status = ri.reason;
829     ai.jobid = ri.qfile;
830     ai.jobtag = "";
831     ai.callid = ri.callid;
832     ai.owner = "";
833     ri.params.asciiEncode(ai.faxdcs);
834     if (!ai.record("RECV"))
835 	logError("Error writing RECV accounting record, dest=%s",
836 	    (const char*) ai.dest);
837 }
838 
839 /*
840  * Handle notification that a document has been received.
841  */
842 void
notifyRecvDone(FaxRecvInfo & ri)843 faxGettyApp::notifyRecvDone(FaxRecvInfo& ri)
844 {
845     FaxServer::notifyRecvDone(ri);
846 
847     fxStr callid_formatted;
848     for (u_int i = 0; i < ri.callid.size(); i++) {
849 	callid_formatted.append(quote | ri.callid.id(i) | enquote);
850     }
851     // hand to delivery/notification command
852     fxStr cmd(faxRcvdCmd
853 	| quote |             ri.qfile	| enquote
854 	| quote |   getModemDeviceID()	| enquote
855 	| quote |            ri.commid	| enquote
856 	| quote |            ri.reason	| enquote
857 	| callid_formatted);
858     traceServer("RECV FAX: %s", (const char*) cmd);
859     setProcessPriority(BASE);			// lower priority
860     runCmd(cmd, true, this);
861     setProcessPriority(state);			// restore priority
862 }
863 
864 /*
865  * Send a modem status message to the central queuer process.
866  */
867 bool
sendModemStatus(const char * msg)868 faxGettyApp::sendModemStatus(const char* msg)
869 {
870     return faxApp::sendModemStatus(getModemDeviceID(), msg);
871 }
872 
873 /*
874  * FIFO-related support.
875  */
876 
877 /*
878  * Open the requisite FIFO special files.
879  */
880 void
openFIFOs()881 faxGettyApp::openFIFOs()
882 {
883     devfifo = openFIFO(fifoName | "." | getModemDeviceID(), 0600, true);
884     Dispatcher::instance().link(devfifo, Dispatcher::ReadMask, this);
885 }
886 
887 void
closeFIFOs()888 faxGettyApp::closeFIFOs()
889 {
890     Sys::close(devfifo), devfifo = -1;
891 }
892 
893 /*
894  * Respond to input on the specified file descriptor.
895  */
896 int
inputReady(int fd)897 faxGettyApp::inputReady(int fd)
898 {
899     if (fd == devfifo)
900 	return FIFOInput(fd);
901     else {
902 	if (state == LISTENING)
903 	    listenForRing();
904 	else
905 	    listenBegin();
906 	return (0);
907     }
908 }
909 
910 /*
911  * Process a message received through a FIFO.
912  */
913 void
FIFOMessage(const char * cp)914 faxGettyApp::FIFOMessage(const char* cp)
915 {
916     switch (cp[0]) {
917     case 'A':				// answer the phone
918 	traceServer("ANSWER %s", cp[1] != '\0' ? cp+1 : "any");
919 	if (cp[1] != '\0' && !streq(cp+1, "any")) {
920 	    if (streq(cp+1, "fax"))
921 		answerPhoneCmd(ClassModem::ANSTYPE_FAX);
922 	    else if (streq(cp+1, "data"))
923 		answerPhoneCmd(ClassModem::ANSTYPE_DATA);
924 	    else if (streq(cp+1, "voice"))
925 		answerPhoneCmd(ClassModem::ANSTYPE_VOICE);
926 	    else if (streq(cp+1, "extern"))
927 		answerPhoneCmd(ClassModem::ANSTYPE_EXTERN);
928 	    else if (strncmp(cp+1, "dial", 4) == 0) {
929 		/*
930 		 * In order to accomodate some so-called "polling" servers which
931 		 * do not follow spec in that we have to dial in, but begin the
932 		 * poll with ATA and follow fax reception protocol rather than
933 		 * polling protocol.  (Which essentially requires us to not
934 		 * produce typical calling CNG beeps, but rather a CED squelch.)
935 		 * We must terminate the dialstring with a semicolon, which
936 		 * should instruct the modem to return "OK" after dialing and not
937 		 * produce CNG, at which time we would follow with ATA.
938 		 */
939 		const char* dialnumber = cp+5;		// usually must end with ";"
940 		answerPhoneCmd(ClassModem::ANSTYPE_DIAL, dialnumber);
941 	    }
942 	} else
943 	    answerPhoneCmd(answerRotary[0]);
944 	break;
945     case 'C':				// configuration control
946 	traceServer("CONFIG \"%s\"", cp+1);
947 	readConfigItem(cp+1);
948 	break;
949     case 'H':				// HELLO from queuer
950 	traceServer("HELLO");
951 	sendModemStatus("N" | getModemNumber());
952 	if (state == FaxServer::RUNNING)
953 	    notifyModemReady();		// sends capabilities also
954 	break;
955     case 'Q':				// quit
956 	traceServer("QUIT");
957 	close();
958 	break;
959     case 'S':				// set modem ready state
960 	traceServer("STATE \"%s\"", cp+1);
961 	setConfigItem("modemreadystate", cp+1);
962 	break;
963     case 'L':				// set modem ready state
964 	traceServer("LOCKWAIT");
965 	discardModem(false);
966 	changeState(LOCKWAIT, pollLockWait);
967 	break;
968     case 'Z':				// abort send/receive
969 	FaxServer::abortSession(Status(301, "Receive aborted due to operator intervention"));
970 	break;
971     default:
972 	faxApp::FIFOMessage(cp);
973 	break;
974     }
975 }
976 
977 /*
978  * Miscellaneous stuff.
979  */
980 
981 /*
982  * Configuration support.
983  */
984 
985 void
resetConfig()986 faxGettyApp::resetConfig()
987 {
988     FaxServer::resetConfig();
989     setupConfig();
990 }
991 
992 #define	N(a)	(sizeof (a) / sizeof (a[0]))
993 
994 faxGettyApp::stringtag faxGettyApp::strings[] = {
995 { "gettyargs",		&faxGettyApp::gettyArgs },
996 { "vgettyargs",		&faxGettyApp::vgettyArgs },
997 { "egettyargs",		&faxGettyApp::egettyArgs },
998 { "faxrcvdcmd",		&faxGettyApp::faxRcvdCmd,	FAX_FAXRCVDCMD },
999 { "dynamicconfig",	&faxGettyApp::dynamicConfig },
1000 };
1001 faxGettyApp::numbertag faxGettyApp::numbers[] = {
1002 { "answerbias",		&faxGettyApp::answerBias,	(u_int) -1 },
1003 };
1004 faxGettyApp::booltag faxGettyApp::booleans[] = {
1005 { "adaptiveanswer",	&faxGettyApp::adaptiveAnswer,	false },
1006 { "lockdatacalls",	&faxGettyApp::lockDataCalls,	true },
1007 { "lockvoicecalls",	&faxGettyApp::lockVoiceCalls,	true },
1008 { "lockexterncalls",	&faxGettyApp::lockExternCalls,	true },
1009 { "logcalls",		&faxGettyApp::logCalls,		true },
1010 { "rejectcall",		&faxGettyApp::rejectCall,	false },
1011 };
1012 
1013 void
setupConfig()1014 faxGettyApp::setupConfig()
1015 {
1016     int i;
1017 
1018     for (i = N(strings)-1; i >= 0; i--)
1019 	(*this).*strings[i].p = (strings[i].def ? strings[i].def : "");
1020     for (i = N(numbers)-1; i >= 0; i--)
1021 	(*this).*numbers[i].p = numbers[i].def;
1022     for (i = N(booleans)-1; i >= 0; i--)
1023 	(*this).*booleans[i].p = booleans[i].def;
1024     readyState = "R";			// default is really ready
1025     modemPriority = 255;		// default is lowest priority
1026     ringsBeforeAnswer = 0;		// default is not to answer phone
1027     setAnswerRotary("any");		// answer calls as ``any''
1028 }
1029 
1030 bool
setConfigItem(const char * tag,const char * value)1031 faxGettyApp::setConfigItem(const char* tag, const char* value)
1032 {
1033     u_int ix;
1034     if (findTag(tag, (const tags*) strings, N(strings), ix)) {
1035 	(*this).*strings[ix].p = value;
1036     } else if (findTag(tag, (const tags*)numbers, N(numbers), ix)) {
1037 	(*this).*numbers[ix].p = getNumber(value);
1038 	switch (ix) {
1039 	case 0: answerBias = fxmin(answerBias, (u_int) 2); break;
1040 	}
1041     } else if (findTag(tag, (const tags*)booleans, N(booleans), ix)) {
1042 	(*this).*booleans[ix].p = getBoolean(value);
1043     } else if (streq(tag, "ringsbeforeanswer")) {
1044 	setRingsBeforeAnswer(getNumber(value));
1045     } else if (streq(tag, "answerrotary")) {
1046 	setAnswerRotary(value);
1047     } else if (streq(tag, "modempriority")) {
1048 	u_int p = getNumber(value);
1049 	if (p != modemPriority) {
1050 	    modemPriority = p;
1051 	    if (state == FaxServer::RUNNING)
1052 		notifyModemReady();
1053 	}
1054     } else if (streq(tag, "modemreadystate")) {
1055 	if (readyState != value) {
1056 	    readyState = value;
1057 	    if (state == FaxServer::RUNNING)
1058 		notifyModemReady();
1059 	}
1060     } else
1061 	return (FaxServer::setConfigItem(tag, value));
1062     return (true);
1063 }
1064 #undef	N
1065 
1066 /*
1067  * Process an answer rotary spec string.
1068  */
1069 void
setAnswerRotary(const fxStr & value)1070 faxGettyApp::setAnswerRotary(const fxStr& value)
1071 {
1072     u_int i;
1073     u_int l = 0;
1074     for (i = 0; i < 3 && l < value.length(); i++) {
1075 	fxStr type(value.token(l, " \t"));
1076 	type.raisecase();
1077 	if (type == "FAX")
1078 	    answerRotary[i] = ClassModem::ANSTYPE_FAX;
1079 	else if (type == "DATA")
1080 	    answerRotary[i] = ClassModem::ANSTYPE_DATA;
1081 	else if (type == "VOICE")
1082 	    answerRotary[i] = ClassModem::ANSTYPE_VOICE;
1083 	else if (type == "EXTERN")
1084 	    answerRotary[i] = ClassModem::ANSTYPE_EXTERN;
1085 	else {
1086 	    if (type != "ANY")
1087 		configError("Unknown answer type \"%s\"", (const char*) type);
1088 	    answerRotary[i] = ClassModem::ANSTYPE_ANY;
1089 	}
1090     }
1091     if (i == 0)				// void string
1092 	answerRotary[i++] = ClassModem::ANSTYPE_ANY;
1093     answerRotor = 0;
1094     answerRotorSize = i;
1095 }
1096 
1097 static void
usage(const char * appName)1098 usage(const char* appName)
1099 {
1100     faxApp::fatal("usage: %s [-c config-item] [-q queue-dir] [-Dpx] modem-device", appName);
1101 }
1102 
1103 static void
sigCleanup(int s)1104 sigCleanup(int s)
1105 {
1106     logError("CAUGHT SIGNAL %d", s);
1107     faxGettyApp::instance().close();
1108     _exit(-1);
1109 }
1110 
1111 int
main(int argc,char ** argv)1112 main(int argc, char** argv)
1113 {
1114     faxApp::setupLogging("FaxGetty");
1115 
1116     fxStr appName = argv[0];
1117     u_int l = appName.length();
1118     appName = appName.tokenR(l, '/');
1119 
1120     faxGettyApp::setupPermissions();
1121 
1122     faxApp::setOpts("c:q:Ddpx");		// p+x are for ModemServer
1123 
1124     fxStr queueDir(FAX_SPOOLDIR);
1125     fxStr device;
1126     GetoptIter iter(argc, argv, faxApp::getOpts());
1127     for (; iter.notDone(); iter++)
1128 	switch (iter.option()) {
1129 	case 'q': queueDir = iter.optArg(); break;
1130 	case '?': usage(appName);
1131 	}
1132     if (device == "") {
1133 	device = iter.getArg();
1134 	if (device == "")
1135 	    usage(appName);
1136     }
1137     if (device[0] != '/')				// for getty
1138 	device.insert(_PATH_DEV);
1139     if (Sys::chdir(queueDir) < 0)
1140 	faxApp::fatal(queueDir | ": Can not change directory");
1141 
1142     faxGettyApp* app = new faxGettyApp(device, faxApp::devToID(device));
1143 
1144     signal(SIGTERM, fxSIGHANDLER(sigCleanup));
1145     signal(SIGINT, fxSIGHANDLER(sigCleanup));
1146 
1147     app->initialize(argc, argv);
1148     app->open();
1149     while (app->isRunning())
1150 	Dispatcher::instance().dispatch();
1151     app->close();
1152     delete app;
1153 
1154     return 0;
1155 }
1156