/* * modem.c - Routines to setup the serial port and talk to the modem * * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: modem.c,v 1.2 2004/04/05 03:20:48 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ttyd.h" extern struct linedef ttyconfig[MAXCONFIG]; extern struct optiondef options; extern int chatpid; /* * Routine to check if a lock file is valid * Returns 1 for a valid lockfile and 0 for a bad one or error */ int check_lock(char *lck_file,pid_t *pid) { char pid_str[12]; int lck_fd,n; memset(pid_str, '\0', 12); /* Check for a stale lock */ lck_fd = open(lck_file, O_RDONLY, 0); if(!lck_fd) { return(0); } n = read(lck_fd, &pid_str, 11); close(lck_fd); if(n < 0) { return(0); } /* See if the process still exists */ sscanf(pid_str, " %d", pid); /* syslog(LOG_NOTICE,"PID.KILL.ERRNO: %d.%d.%d", *pid, kill(*pid,0),errno); if(pid == 0 || (kill(*pid, 0) < 0 && errno == ESRCH)) { */ if(pid == 0 || kill(*pid, 0) < 0 ) { /* Stale lock file */ syslog(LOG_NOTICE,"Process still exists"); return(1); } else { /* Valid lock file */ return(0); } } /* * Routine to make up a lockfile for the port. It will break a stale * lockfile if it finds it or return -1 if there is already a lockfile */ int lock_port(struct slinedef *port) { int lck_fd; char lck_file[1024],*tmp; pid_t pid; char pid_str[12]; memset(pid_str, '\0', 12); if (strchr(ttyconfig[port->confignum].linename,'/')!=NULL) { tmp = basename(ttyconfig[port->confignum].linename); sprintf(lck_file, "%s%s", LOCK_PREFIX, tmp); } else { sprintf(lck_file, "%s%s", LOCK_PREFIX, ttyconfig[port->confignum].linename); } syslog(LOG_NOTICE,"Lockfile name: %s", lck_file); while(1) { if ((lck_fd = open(lck_file, O_RDWR | O_EXCL | O_CREAT, 0644))<0) { if(errno == EEXIST) { if (check_lock(lck_file,&pid)) { syslog(LOG_NOTICE,"%s: Port locked by pid %d", ttyconfig[port->confignum].linename,pid); return(1); } else { /* Unlink the old lock */ unlink(lck_file); syslog(LOG_NOTICE,"%s: Removed stale lock, pid %d",ttyconfig[port->confignum].linename, pid); continue; } } else { syslog(LOG_NOTICE,"%s: Failed to lock port: %s %s", ttyconfig[port->confignum].linename,lck_file,strerror(errno)); return(1); } } /* Write our lock info */ pid = getpid(); sprintf(pid_str, "%010d\n", pid); write(lck_fd, pid_str, 11); close(lck_fd); return(0); } } /* * Routine to remove the lock file */ void unlock_port(struct slinedef *port) { char lck_file[1024],*tmp; if (strchr(ttyconfig[port->confignum].linename,'/')!=NULL) { tmp = basename(ttyconfig[port->confignum].linename); snprintf(lck_file, 1024, "%s%s", LOCK_PREFIX, tmp); } else { snprintf(lck_file, 1024, "%s%s", LOCK_PREFIX, ttyconfig[port->confignum].linename); } unlink(lck_file); } /* * Routine to open the serial port and get a lockfile on it */ int open_port(struct slinedef *port) { char tmp[1024]; int fd, fl; /* Create a lock file is possible */ if (lock_port(port)>0) { return(1); } /* Open the connection to the serial port */ if (ttyconfig[port->confignum].linename[0]=='/') { strncpy(tmp,ttyconfig[port->confignum].linename,1023); } else { snprintf(tmp,1023,"/dev/%s",ttyconfig[port->confignum].linename); } if ((fd = open(tmp, O_RDWR | O_EXCL | O_NONBLOCK, 0))<0) { syslog(LOG_NOTICE,"Error opening serial port %s: %s",tmp,strerror(errno)); return(1); } port->fd=fd; /* drain any characters waiting */ while (read(fd, tmp, sizeof(tmp)) > 0) ; /* put the fd back into blocking mode */ fl = fcntl(port->fd, F_GETFL, 0); fl &= ~O_NONBLOCK; fcntl(port->fd, F_SETFL, fl); return(0); } /* * Routine to configure the serial port so we can talk to it */ int init_port(struct slinedef *port) { struct termios tios; /* Get the current line settings */ if(tcgetattr(port->fd, &tios)) { syslog(LOG_NOTICE,"Unable to get port %s's setting: %s", ttyconfig[port->confignum].linename,strerror(errno)); return -errno; } /* Modify for PPP */ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL); tios.c_cflag |= CS8 | CREAD | HUPCL; tios.c_iflag = IGNBRK | IGNPAR; tios.c_oflag = 0; tios.c_lflag = 0; tios.c_cc[VMIN] = 1; tios.c_cc[VTIME] = 0; /* Remove modem control if direct */ if(ttyconfig[port->confignum].type == TYPE_LOCAL) { tios.c_cflag ^= (CLOCAL | HUPCL); syslog(LOG_NOTICE,"Port %s set to local mode", ttyconfig[port->confignum].linename); } /* Set flow control */ if(ttyconfig[port->confignum].flow == FLOW_RTS) { tios.c_cflag |= CRTSCTS; syslog(LOG_NOTICE,"Port %s set to RTSCTS", ttyconfig[port->confignum].linename); } else { tios.c_iflag |= IXOFF; tios.c_cc[VSTOP] = 0x13; tios.c_cc[VSTART] = 0x11; syslog(LOG_NOTICE,"Port %s set to XONOFF", ttyconfig[port->confignum].linename); } /* Set the speed */ if (cfsetospeed (&tios, ttyconfig[port->confignum].speed)<0) { syslog(LOG_NOTICE,"Can't set out baudrate: %s",strerror(errno)); } if (cfsetispeed (&tios, ttyconfig[port->confignum].speed)<0) { syslog(LOG_NOTICE,"Can't set in baudrate: %s",strerror(errno)); } /* Set the attributes */ if(tcsetattr(port->fd, TCSAFLUSH, &tios) < 0) { syslog(LOG_NOTICE,"Configuring of port %s failed: %s", ttyconfig[port->confignum].linename,strerror(errno)); return(errno); } return(0); } /* * Routine to execute chat and open a connection to the remove site with the wonderous * AT commands. This should be replaced with a routine to talk to the serial port directly * as this would be 100 times more efficient. */ int do_chat(struct slinedef *port,char *connect) { int status; int efd; pid_t pid; char command[1024]; /* Make up our command to pass to /bin/sh */ sprintf(command,"%s %s %s",options.chatprog, options.chatparams, connect); /* fork and chat */ #ifdef DEBUG syslog(LOG_NOTICE,"Running: %s",command); #endif #ifndef NOFORK pid = fork(); if(pid < 0) { syslog(LOG_NOTICE,"%s: fork", ttyconfig[port->confignum].linename); return(errno); } if(pid == 0) { #endif /* Make the serial port stdin and stdout */ dup2(port->fd, 0); dup2(port->fd, 1); /* Make stderr the log file */ efd = open("/dev/null", O_RDWR); dup2(efd, 2); /* run chat - should be fixed not to run sh! */ execlp("/bin/sh","/bin/sh","-c",command,(char *)0); /* We only get here on error */ exit(-errno); #ifndef NOFORK } chatpid=pid; while(waitpid(pid, &status, 0) < 0) { if(errno == EINTR) continue; } if (status==0) { syslog(LOG_NOTICE,"Chat on port %s completed sucessfully\n", ttyconfig[port->confignum].linename); } else { syslog(LOG_NOTICE,"Chat on port %s failed. Exit code %d\n", ttyconfig[port->confignum].linename,WEXITSTATUS(status)); } #endif return(status); } /* * Routine to find the first available serial port to dial out on. Returns the * config item number if an available port is found, otherwise it returns -1 */ int findserial(int portnum) { int i, t; char lck_file[1024],*tmp; pid_t pid; /* Check if the port number in the config file has an available serial port */ /* This allows the user to select the port to dial out on */ if (portnum