ftp.nice.ch/pub/next/unix/communication/pcomm.NIHS.bs.tar.gz#/pcomm/Source/port.c

This is port.c in view mode; [Download] [Up]

/*
 * Routines to get or release a TTY port.
 */

#define MAX_PID	30000
#define TRUE	1
#define FALSE	0

#include <stdio.h>
#include <errno.h>
#include "config.h"
#include "dial_dir.h"
#include "modem.h"

#ifdef BSD
#include <sys/file.h>
#else /* BSD */
#include <fcntl.h>
#endif /* BSD */

#ifdef UNIXPC
#include <sys/phone.h>
#endif /* UNIXPC */

#ifdef XENIX_LOCKS
#include <ctype.h>
#endif /* XENIX_LOCKS */

#ifdef SVR4_LOCKS
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#endif /* SVR4_LOCKS */

static int getty_status = 0;
static char *lock_path = NULL;
/*
 * Finds a free (or requested) serial port.  Creates a lock file to hold
 * for our use.  Loads the modem database.  A non-zero return code means
 * all ports (or the requested port) are busy.
 */

int
get_port()
{
	extern int fd;
	register int i;
	int j, k, lfd, list[NUM_TTY], cmask, is_dev, progpid;
	static int set_getty(), checklock(), ck_speed();
	char file[80], buf[80], message[80], *str_rep(), *last_c;
	void error_win(), line_set(), release_port(), send_str();

	is_dev = chk_script(dir->script[dir->d_cur]);
	/*
	 * If you already have a port, see if it is good enough for the
	 * current request.
	 */
#ifdef KEEP_PORT
	if (fd != -1) {
		if (!strcmp(dir->script[dir->d_cur], modem->tty[modem->t_cur])
		 || (!is_dev && ck_speed(modem->t_cur, dir->baud[dir->d_cur]))){
			/*
			 * Reset the line because the baud rate (or other
			 * parameters) may have changed.
			 */
			line_set();
			return(0);
		}
	}
#endif /* KEEP_PORT */

	release_port(VERBOSE);

	list[0] = -1;
	/*
	 * See if you want a specific TTY port.  If the script field in the
	 * dialing directory is a valid device name, then use that TTY.
	 */
	if (is_dev) {
		for (i=0; i<modem->t_entries; i++) {
					/* and it exists in modem database */
			if (!strcmp(dir->script[dir->d_cur], modem->tty[i])) {
				list[0] = i;
				list[1] = -1;
				break;
			}
		}
					/* oops... we don't know that port */
		if (list[0] == -1) {
			sprintf(message, "Device \"%s\" in the script field doesn't exist in", dir->script[dir->d_cur]);
			sprintf(buf, "modem/TTY database \"%s\"", modem->m_path);
			error_win(0, message, buf);
			return(1);
		}
	}

	/*
	 * Create a list of acceptable TTYs.  It searches the modem database
	 * for the requested baud rate.
	 */
	k = 0;
	if (list[0] == -1) {
		for (i=0; i<modem->t_entries; i++) {
					/* skip ports with no modems */
			if (!strcmp(modem->tname[i], "DIRECT"))
				continue;

					/* can handle requested baud rate? */
			if (ck_speed(i, dir->baud[dir->d_cur]))
				list[k++] = i;
		}
					/* the end of list marker */
		list[k] = -1;
	}
					/* empty list? */
	if (list[0] == -1) {
		sprintf(message, "No modem at a %d baud rating exists in the", dir->baud[dir->d_cur]);
		sprintf(buf, "modem database \"%s\"", modem->m_path);
		error_win(0, message, buf);
		return(1);
	}
					/* check the list for a free port */
	i = 0;
	while (list[i] != -1) {
					/* create a lock file name */
#ifdef SVR4_LOCKS
		struct stat sbuf;
		sprintf(buf, "/dev/%s", modem->tty[list[i]]);
		stat(buf, &sbuf);
		sprintf(file, "%s/LK.%03d.%03d.%03d", LOCK_DIR, major(sbuf.st_dev), major(sbuf.st_rdev), minor(sbuf.st_rdev));
#else /* SVR4_LOCKS */
		sprintf(file, "%s/LCK..%s", LOCK_DIR, modem->tty[list[i]]);
#endif /* SVR4_LOCKS */

#ifdef XENIX_LOCKS
		last_c = file + strlen(file)-1;
		if (isupper(*last_c))
			*last_c = tolower(*last_c);
#endif /* XENIX_LOCKS */

#ifdef DEBUG
		fprintf(stderr, "get_port: checking '/dev/%s'\n", modem->tty[list[i]]);
#endif /* DEBUG */

					/* no lock exists or it is dead */
		if (checklock(file)) {
			getty_status = set_getty(modem->tty[list[i]], FALSE);

			cmask = umask(0);
			if ((lfd = open(file, O_CREAT|O_EXCL|O_WRONLY, 0666)) < 0) {
				if (getty_status)
					set_getty(modem->tty[list[i]], TRUE);
				sprintf(buf, "\"%s\"", file);
				error_win(0, "Can't create the lockfile", buf);
				return(1);
			}
			umask(cmask);
#ifdef ASCII_PID
			sprintf(buf, "%10d\n", getpid());
			write(lfd, buf, 11);
#else /* ASCII_PID */
			progpid = getpid();
			write(lfd, (char *) &progpid, sizeof(int));
#endif /* ASCII_PID */
			close(lfd);
					/* store the new values */
			lock_path = str_rep(lock_path, file);
			modem->t_cur = list[i];

					/* load the modem data base */
			modem->m_cur = -1;
			for (j=0; j<modem->m_entries; j++) {
				if (!strcmp(modem->tname[modem->t_cur], modem->mname[j])) {
					modem->m_cur = j;
					break;
				}
			}
			if (modem->m_cur == -1) {
				sprintf(buf, "Modem name \"%s\" in TTY database",
				 modem->tname[modem->t_cur]);
				error_win(0, buf, "does not exist in modem database");
				modem->t_cur = -1;
				return(1);
			}

					/* open the device (ignore DCD) */
			sprintf(buf, "/dev/%s", modem->tty[modem->t_cur]);
			if ((fd = open(buf, O_RDWR|O_NDELAY)) < 0) {
				if (getty_status)
					set_getty(modem->tty[modem->t_cur], TRUE);
				sprintf(file, "Can't open port \"%s\" for read and write", buf);
				error_win(0, file, "");
				modem->m_cur = -1;
				modem->t_cur = -1;
				return(1);
			}
					/* change line settings */
			line_set();
					/* ...just to be sure */
			close(open(buf, O_RDWR));

					/* turn off the O_NDELAY setting */
			tty_noblock(fd, FALSE);

					/* initialize the modem */
			send_str(modem->init[modem->m_cur], SLOW);
			return(0);
		}
		i++;
	}
	error_win(0, "All ports are busy now, try again later", "");
	return(1);
}

/*
 * Release the port.  Closes the file descriptor and removes the
 * lock file
 */

void
release_port(verbose)
int verbose;
{
	extern int fd;
	extern char *null_ptr;
	char buf[80];
	void free_ptr(), hang_up(), reset_line(), error_win();

	/*
	 * The modem structure can't be guaranteed to exist yet.  For example,
	 * an error in reading one of the other support files would cause
	 * this routine to be used before the MODEM structure gets allocated.
	 */
	if (modem == NULL)
		return;
					/* close the port */
	if (fd != -1) {
		tty_flush(fd, 2);
		/*
		 * Since HUPCL is set, the close() should drop the DTR and
		 * hang up the modem (provided you've got the modem to
		 * respond to DTR).  Since this is not guaranteed, we send
		 * the hang_up string first.
		 */
		hang_up(verbose);
		reset_line();
		close(fd);
	}
					/* remove the lock */
	if (lock_path != NULL && *lock_path != '\0') {
		if (unlink(lock_path)) {
			sprintf(buf, "\"%s\"", lock_path);
			error_win(0, "Can't remove the lock file", buf);
		}
		free_ptr(lock_path);
		lock_path = null_ptr;
	}
					/* turn the getty back on? */
	if (getty_status && modem->t_cur != -1)
		set_getty(modem->tty[modem->t_cur], TRUE);
					/* clean up the structure */
	fd = -1;
	modem->m_cur = -1;
	modem->t_cur = -1;
	return;
}

/*
 * Turn the /etc/getty on or off for the specified port.  A non-zero return
 * code means that the getty was on.  Systems with uugetty() or dedicated
 * dialout ports won't need this routine.
 */

/* ARGSUSED */
static int
set_getty(tty, on)
char *tty;
int on;
{
#ifdef UNIXPC
	int i, ret_code;
	char buf[40];
	unsigned int sleep();
					/* the last three characters */
	i = strlen(tty) -3;

	ret_code = 0;
	if (on) {
		sprintf(buf, "setgetty %s 1", tty+i);
		system(buf);
	}
	else {
		sprintf(buf, "setgetty %s 0", tty+i);
		if (system(buf) == 512)
			ret_code++;
		sleep(1);
	}
	return(ret_code);
#else /* UNIXPC */
	/*
	 * If you don't have one of these cute little routines, you
	 * might wanna write one.  It should check for an existing lock
	 * file, edit the /etc/inittab file, and issue an init -q.
	 * The return code should tell if there was a getty or not.
	 * Obviously the program would be suid to root.
	 */
	return(0);
#endif /* UNIXPC */
}

/*
 * Check the lock file for a valid pid value.  Error conditions such
 * as not being able to open the lock file or not being able to interpret
 * the contents of the lock file cause the code to assume that the lock
 * file is valid.  Let the user worry about weird special cases.  A 
 * non-zero return code means the lock is dead or doesn't exist.
 */

static int
checklock(lockfile)
char *lockfile;
{
	extern int errno;
	int lfd, lckpid, n;
	unsigned int sleep();
	char buf[40];
					/* doesn't exist */
	if (access(lockfile, 0))
		return(1);
					/* can't open the lock file */
	if ((lfd = open(lockfile, 0)) < 0)
		return(0);

#ifdef ASCII_PID
	if ((n = read(lfd, buf, 40)) <= 0) {
		close(lfd);
		return(0);
	}
	close(lfd);
	buf[n--] = '\0';
	lckpid = atoi(buf);
#else /* ASCII_PID */
	if (read(lfd, (char *) &lckpid, sizeof(int)) != sizeof(int)) {
		close(lfd);
		return(0);
	}
	close(lfd);
#endif /* ASCII_PID */
					/* invalid pid? */
	if (lckpid <= 0 || lckpid > MAX_PID)
		return(0);

	if ((kill(lckpid, 0) == -1) && (errno == ESRCH)) {
		/*
		 * If the kill was unsuccessful due to an ESRCH error,
		 * that means the process is no longer active and the
		 * lock file can be safely removed.
		 */
		unlink(lockfile);
		sleep(1);
		return(1);
	}
	/*
	 * If the kill() was successful, that means the process must
	 * still be active.
	 */
	return(0);
}

/*
 * Check to see if the desired baud rate can be handled by the modem.
 * Uses the connect strings to make this determination.  The first
 * argument is the index into the TTY database.  A non-zero return code
 * means "yes it can".
 */

static int
ck_speed(tty, baud)
int tty, baud;
{
	int i, mod;
	char buf[60];
	void error_win();
					/* find the modem database */
	mod = -1;
	for (i=0; i<modem->m_entries; i++) {
		if (!strcmp(modem->mname[i], modem->tname[tty])) {
			mod = i;
			break;
		}
	}
	if (mod == -1) {
		sprintf(buf, "Modem name \"%s\" in TTY database", modem->tname[tty]);
		error_win(1, buf, "does not exist in modem database");
	}

#ifdef DEBUG
	fprintf(stderr, "ck_speed: checking modem \"%s\" for %d baud\n", modem->mname[mod], baud);
#endif /* DEBUG */

	switch (baud) {
		case 300:
			if (*modem->con_3[mod] != '\0')
				return(1);
			break;
		case 1200:
			if (*modem->con_12[mod] != '\0')
				return(1);
			break;
		case 2400:
			if (*modem->con_24[mod] != '\0')
				return(1);
			break;
		case 4800:
			if (*modem->con_48[mod] != '\0')
				return(1);
			break;
		case 9600:
			if (*modem->con_96[mod] != '\0')
				return(1);
			break;
		case 19200:
			if (*modem->con_192[mod] != '\0')
				return(1);
			break;
	}
	return(0);
}

/*
 * Check to see if the script field is a valid device name.
 */

chk_script(script)
char *script;
{
	char buf[80], *strcpy(), *strcat();

	if (*script == '\0')
		return(0);

	strcpy(buf, "/dev/");
	strcat(buf, script);

	if (!access(buf, 0))
		return(1);
	return(0);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.