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