static char sccsid[] = "$Id: mg_m_init.c,v 4.12 2006/01/03 10:15:37 gert Exp $ Copyright (c) Gert Doering"; /* mg_m_init.c - part of mgetty+sendfax * * Initialize (fax-) modem for use with mgetty */ #include #include "syslibs.h" #include #include #include #include #include #ifndef sunos4 #include #endif #ifdef linux # include typedef u_int32_t __u32; # include #endif #include "mgetty.h" #include "tio.h" #include "policy.h" #include "fax_lib.h" #if (defined(M_XENIX) && !defined(M_UNIX)) || defined(NEXTSGTTY) #define O_NOCTTY 0 #endif chat_action_t init_chat_actions[] = { { "ERROR", A_FAIL }, { "BUSY", A_FAIL }, { "NO CARRIER", A_FAIL }, { NULL, A_FAIL } }; static int init_chat_timeout = 20; /* initialize data section */ int mg_init_data _P4( (fd, chat_seq, need_dsr, force_chat_seq), int fd, char * chat_seq[], boolean need_dsr, char * force_chat_seq[] ) { action_t what_action; if ( do_chat( fd, chat_seq, init_chat_actions, &what_action, init_chat_timeout, TRUE ) == SUCCESS ) { return SUCCESS; } /* maybe the modem init failed, because the modem was switched * off. So, we check now that there is a DSR or a CTS signal * coming from the modem - and if not, we sleep until it comes back. * WARNING: this can fail on systems not allowing to read out the * RS232 status lines, thus it is optional, and off by default! */ if ( need_dsr ) { int rs_lines = tio_get_rs232_lines(fd); if ( rs_lines != -1 && (rs_lines & (TIO_F_DSR|TIO_F_CTS) ) == 0 ) { lprintf( L_WARN, "No DSR/CTS signals, assuming modem is switched off, waiting..." ); while( (tio_get_rs232_lines(fd) & (TIO_F_DSR|TIO_F_CTS) ) == 0) { sleep(60); } } } /* if init_chat failed because the modem didn't respond, and we have * a "force_chat" sequence, try this. * (force_chat might contain DLE ETX for voice modems, or just plain * simple +++ATH0 for data modems (mis-)configured with AT&D0) */ if ( what_action == A_TIMOUT && force_chat_seq != NULL ) { lprintf( L_WARN, "init chat timed out, trying force-init-chat" ); if ( do_chat( fd, force_chat_seq, init_chat_actions, &what_action, init_chat_timeout, TRUE ) == SUCCESS || what_action != A_TIMOUT ) { lprintf( L_NOISE, "force-init succeeded, retrying init-chat"); clean_line(fd, 3); if ( do_chat( fd, chat_seq, init_chat_actions, &what_action, init_chat_timeout, TRUE ) == SUCCESS ) { return SUCCESS; } } } /* either no force_chat available, or that didn't help either: BARF! */ errno = ( what_action == A_TIMOUT )? EINTR: EINVAL; lprintf( L_ERROR, "init chat failed, exiting..." ); return FAIL; } /* initialization stuff for fax */ /* initialize fax section */ int mg_init_fax _P5( (fd, mclass, fax_id, fax_only, fax_max_speed), int fd, char * mclass, char * fax_id, boolean fax_only, int fax_max_speed ) { /* find out whether this beast is a fax modem... */ modem_type = fax_get_modem_type( fd, mclass ); if ( modem_type == Mt_data ) { lprintf( L_NOISE, "no class 2/2.0 faxmodem, no faxing available" ); return FAIL; } if ( modem_type == Mt_class1 || modem_type == Mt_class1_0 ) { /* set adaptive answering */ char aabuf[20]; sprintf( aabuf, "AT+FA%s=%d", modem_type == Mt_class1? "E": "A", fax_only? 0:1 ); if ( mdm_command( aabuf, fd ) == FAIL ) { lprintf( L_MESG, "cannot set answer/reception flags" ); } fax_set_bor( fd, 1 ); /* can't fail */ } if ( modem_type == Mt_class2_0 ) { /* set adaptive answering, bit order, receiver on */ if ( mdm_command( fax_only? "AT+FAA=0;+FCR=1": "AT+FAA=1;+FCR=1", fd ) == FAIL ) { lprintf( L_MESG, "cannot set answer/reception flags" ); } if ( fax_set_bor( fd, 1 ) == FAIL ) { lprintf( L_MESG, "cannot set bit order, trying +BOR=0" ); fax_set_bor( fd, 0 ); } /* report everything except NSF (unless asked for it) */ mdm_command( (modem_quirks & MQ_SHOW_NSF)? "AT+FNR=1,1,1,1" : "AT+FNR=1,1,1,0", fd ); } if ( modem_type == Mt_class2 ) { /* even if we know that it's a class 2 modem, set it to * +FCLASS=0: there are some weird modems out there that won't * properly auto-detect fax/data when in +FCLASS=2 mode... * * Exception: Dr.Neuhaus modems do adaptive answering *only* if in * +FCLASS=2 mode -> check flag set by auto-detection */ if ( !fax_only && ! ( modem_quirks & MQ_NEED2 ) ) { if ( mdm_command( "AT+FCLASS=0", fd ) == FAIL ) { lprintf( L_MESG, "weird: cannot set class 0" ); } } /* now, set various flags and modem settings. Failures are logged, but ignored - after all, either the modem works or not, we'll see it when answering the phone ... */ /* set adaptive answering, bit order, receiver on */ if ( mdm_command( fax_only? "AT+FAA=0;+FCR=1": "AT+FAA=1;+FCR=1", fd ) == FAIL ) { lprintf( L_MESG, "cannot set answer/reception flags" ); } if ( fax_set_bor( fd, 0 ) == FAIL ) { lprintf( L_MESG, "cannot set bit order. Huh?" ); } } /* common part for class 2 and class 2.0 */ /* local fax station id */ fax_set_l_id( fd, fax_id ); /* capabilities */ if ( fax_set_fdcc( fd, 1, fax_max_speed, 0 ) == FAIL ) { lprintf( L_MESG, "huh? Cannot set +FDCC parameters" ); } return SUCCESS; } /* initialize fax poll server functions (if possible) */ extern char * faxpoll_server_file; /* in faxrec.c */ void faxpoll_server_init _P2( (fd,f), int fd, char * f ) { faxpoll_server_file = NULL; if ( access( f, R_OK ) != 0 ) { lprintf( L_ERROR, "cannot access/read '%s'", f ); } else if ( mdm_command( modem_type == Mt_class2_0? "AT+FLP=1":"AT+FLPL=1", fd ) == FAIL) { lprintf( L_WARN, "faxpoll_server_init: no polling available" ); } else { faxpoll_server_file = f; lprintf( L_NOISE, "faxpoll_server_init: OK, waiting for poll" ); } } /* open device (non-blocking / blocking) * * open with O_NOCTTY, to avoid preventing dial-out processes from * getting the line as controlling tty */ int mg_open_device _P2 ( (devname, blocking), char * devname, boolean blocking ) { int fd; if ( ! blocking ) { fd = open(devname, O_RDWR | O_NDELAY | O_NOCTTY ); if ( fd < 0 ) { lprintf( L_FATAL, "mod: cannot open line %s", devname ); return ERROR; } /* unset O_NDELAY (otherwise waiting for characters */ /* would be "busy waiting", eating up all cpu) */ fcntl( fd, F_SETFL, O_RDWR); } else /* blocking open */ { again: lprintf( L_MESG, "mod: blocking-open(%s)", devname ); fd = open( devname, O_RDWR | O_NOCTTY ); if ( fd < 0) { if ( errno == EAGAIN ) goto again; lprintf( L_FATAL, "mod: cannot open line %s", devname ); return ERROR; } } #ifdef NEXTSGTTY /* get rid of controlling tty: on NeXT, there is no O_NOCTTY */ ioctl( fd, TIOCNOTTY, 0 ); #endif /* make new fd == stdin if it isn't already */ if (fd > 0) { (void) close(0); if (dup2(fd,0) != 0) { lprintf( L_FATAL, "mod: cannot make %s stdin", devname ); return ERROR; } } /* make stdout and stderr, too */ (void) close(1); (void) close(2); if (dup2(0,1) != 1) { lprintf( L_FATAL, "mod: cannot dup to stdout"); return ERROR; } if (dup2(0,2) != 2) { lprintf( L_FATAL, "mod: cannot dup to stderr"); return ERROR; } if ( fd > 2 ) (void) close(fd); /* switch off stdio buffering */ setbuf(stdin, (char *) NULL); setbuf(stdout, (char *) NULL); setbuf(stderr, (char *) NULL); return NOERROR; } /* init device: toggle DTR (if requested), set TIO values */ int mg_init_device _P4( (fd, toggle_dtr, toggle_dtr_waittime, portspeed ), int fd, boolean toggle_dtr, int toggle_dtr_waittime, unsigned int portspeed ) { TIO tio; if (toggle_dtr) { lprintf( L_MESG, "lowering DTR to reset Modem" ); tio_toggle_dtr( fd, toggle_dtr_waittime ); } #ifdef TIOCSSOFTCAR /* turn off SunOS soft carrier "feature" */ { int off = 0; if ( ioctl( fd, TIOCSSOFTCAR, &off ) < 0 ) lprintf( L_ERROR, "cannot turn off soft carrier" ); } #endif /* initialize port */ if ( tio_get( fd, &tio ) == ERROR ) { lprintf( L_FATAL, "cannot get TIO" ); return ERROR; } tio_mode_sane( &tio, TRUE ); /* initialize all flags */ tio_set_speed( &tio, portspeed ); /* set bit rate */ tio_default_cc( &tio ); /* init c_cc[] array */ tio_mode_raw( &tio ); #ifdef sun /* SunOS does not rx with RTSCTS unless carrier present */ tio_set_flow_control( STDIN, &tio, (DATA_FLOW) & (FLOW_SOFT) ); #else tio_set_flow_control( STDIN, &tio, DATA_FLOW ); #endif if ( tio_set( STDIN, &tio ) == ERROR ) { lprintf( L_FATAL, "cannot set TIO" ); return ERROR; } #ifdef linux /* if port speed is set to 38400, kernel flag might turn it into * 57600 or 115200. Make sure the user knows about it! */ if ( portspeed == 38400 ) { struct serial_struct serinfo; if ( ioctl( STDIN, TIOCGSERIAL, &serinfo ) == 0 && ( serinfo.flags & ASYNC_SPD_MASK ) != 0 ) { lprintf( L_WARN, "WARNING: obsolete setserial spd_hi/spd_vhi used, 38400 is not real port speed" ); } } #endif return NOERROR; } /* open + initialize device * * if first init fails, try again: on Linux and SunOS, the port isn't * able anymore after carrier drop, but after reopening it, it is. */ int mg_get_device _P5( (devname, blocking_open, toggle_dtr, toggle_dtr_waittime, portspeed ), char * devname, boolean blocking_open, boolean toggle_dtr, int toggle_dtr_waittime, unsigned int portspeed) { boolean first_try = TRUE; int rs_lines; /* most likely, HUPCL was set and so DTR is low right now. Give * modem some time to settle down. */ delay(500); /* open device, make it stdin/out/err */ try_again: if ( mg_open_device( devname, blocking_open ) == ERROR ) { lprintf( L_FATAL, "open device %s failed", devname ); return ERROR; } /* catch "standard question #17" (DCD drop -> fd invalid -> I/O error) */ rs_lines = tio_get_rs232_lines(STDIN); if ( rs_lines != -1 ) { #ifdef linux if ( rs_lines & TIO_F_DCD ) lprintf( L_MESG, "WARNING: DCD line still active, check modem settings (AT&Dx)" ); #endif if ( ! (rs_lines & TIO_F_DSR) ) lprintf( L_WARN, "WARNING: DSR is off - modem turned off or bad cable?" ); } /* initialize device (hangup, raw, speed). May fail! */ if ( mg_init_device( STDIN, toggle_dtr, toggle_dtr_waittime, portspeed ) == ERROR ) { if ( first_try ) { lprintf( L_WARN, "mg_init_device failed, trying again" ); first_try = FALSE; goto try_again; } lprintf( L_FATAL, "mg_init_device failed, exiting" ); return ERROR; } return NOERROR; } /* get a given tty as controlling tty * * on many systems, this works with ioctl( TIOCSCTTY ), on some * others, you have to reopen the device */ int mg_get_ctty _P2( (fd, devname), int fd, char * devname ) { /* BSD systems, Linux, *NOT* HP-UX */ #if defined( TIOCSCTTY ) && !defined( _HPUX_SOURCE) if ( setsid() == -1 && errno != EPERM ) { lprintf( L_ERROR, "cannot make myself session leader (setsid)" ); } if ( ioctl( fd, TIOCSCTTY, NULL ) != 0 ) { lprintf( L_ERROR, "cannot set controlling tty (ioctl)" ); if ( getppid() != 1 ) { lprintf( L_WARN, ">>> this might be caused because you have run mgetty/vgetty" ); lprintf( L_WARN, ">>> from the command line. Don't do that, use /etc/inittab!" ); } return ERROR; } #else /* SVR3 and earlier */ fd = open( devname, O_RDWR | O_NDELAY ); if ( fd == -1 ) { lprintf( L_ERROR, "cannot set controlling tty (open)" ); return ERROR; } fcntl( fd, F_SETFL, O_RDWR); /* unset O_NDELAY */ close( fd ); #endif /* !def TIOCSCTTY */ return NOERROR; }