00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054 #include <sys/types.h>
00055 #include <fcntl.h>
00056 #include <termios.h>
00057 #include <signal.h>
00058 #include <stdlib.h>
00059 #include <string.h>
00060 #include <unistd.h>
00061 #include <stdarg.h>
00062 #include <errno.h>
00063 #include <sys/wait.h>
00064 #include <sys/stat.h>
00065
00066 #define CHUNK 1024
00067
00068 char TERM_LINE[] = "/dev/modem";
00069
00070
00071 char lockfile[] = "/usr/spool/locks/LK.iii.jjj.kkk";
00072
00073 char *commdev;
00074 int commfd;
00075 struct termios tccomm;
00076 struct termios tcstdin;
00077 struct termios tcsavestdin;
00078
00079
00080 #define HOTKEY '\035'
00081
00082 struct param_s {
00083 char *pattern;
00084 unsigned value;
00085 enum { BAD, BITS, PARITY, SPEED } type;
00086 } params[] = {
00087 { "5", CS5, BITS },
00088 { "6", CS6, BITS },
00089 { "7", CS7, BITS },
00090 { "8", CS8, BITS },
00091
00092 { "even", PARENB, PARITY },
00093 { "odd", PARENB|PARODD, PARITY },
00094
00095 { "50", B50, SPEED },
00096 { "75", B75, SPEED },
00097 { "110", B110, SPEED },
00098 { "134", B134, SPEED },
00099 { "200", B200, SPEED },
00100 { "300", B300, SPEED },
00101 { "600", B600, SPEED },
00102 { "1200", B1200, SPEED },
00103 { "1800", B1800, SPEED },
00104 { "2400", B2400, SPEED },
00105 { "4800", B4800, SPEED },
00106 { "9600", B9600, SPEED },
00107 { "19200", B19200, SPEED },
00108 { "38400", B38400, SPEED },
00109 { "57600", B57600, SPEED },
00110 { "115200", B115200, SPEED },
00111 { "", 0, BAD },
00112 };
00113
00114 #define NIL ((char *) NULL)
00115
00116 _PROTOTYPE(int main, (int argc, char *argv[]));
00117 _PROTOTYPE(int isdialstr, (char *arg));
00118 _PROTOTYPE(void tell, (int fd, ...));
00119 _PROTOTYPE(void reader, (int on));
00120 _PROTOTYPE(void shell, (char *cmd));
00121 _PROTOTYPE(void lock_device, (char *device));
00122 _PROTOTYPE(void fatal, (char *label));
00123 _PROTOTYPE(void setnum, (char *s, int n));
00124 _PROTOTYPE(void set_uart, (int argc, char *argv[], struct termios *tcp));
00125 _PROTOTYPE(void set_raw, (struct termios *tcp));
00126 _PROTOTYPE(void quit, (int code));
00127
00128 int main(argc, argv)
00129 int argc;
00130 char *argv[];
00131 {
00132 int i;
00133 unsigned char key;
00134 int candial;
00135
00136 for (i = 1; i < argc; ++i) {
00137 if (argv[i][0] == '/') {
00138 if (commdev != NULL) {
00139 tell(2, "term: too many communication devices\n", NIL);
00140 exit(1);
00141 }
00142 commdev = argv[i];
00143 }
00144 }
00145 if (commdev == NULL) commdev = TERM_LINE;
00146
00147
00148 if (tcgetattr(0, &tcsavestdin) < 0) {
00149 tell(2, "term: standard input is not a terminal\n", NIL);
00150 exit(1);
00151 }
00152
00153 lock_device(commdev);
00154
00155 commfd = open(commdev, O_RDWR);
00156 if (commfd < 0) {
00157 tell(2, "term: can't open ", commdev, ": ", strerror(errno), "\n", NIL);
00158 quit(1);
00159 }
00160
00161
00162 if (tcgetattr(commfd, &tccomm) < 0) {
00163 tell(2, "term: ", commdev, " is not a terminal\n", NIL);
00164 quit(1);
00165 }
00166 signal(SIGINT, quit);
00167 signal(SIGTERM, quit);
00168 tcstdin = tcsavestdin;
00169 set_raw(&tcstdin);
00170 set_raw(&tccomm);
00171 set_uart(argc, argv, &tccomm);
00172 tcsetattr(0, TCSANOW, &tcstdin);
00173 tcsetattr(commfd, TCSANOW, &tccomm);
00174
00175
00176 reader(1);
00177
00178
00179 tell(1, "Connected to ", commdev,
00180 ", command key is CTRL-], type ^]? for help\r\n", NIL);
00181
00182
00183 candial = 0;
00184 for (i = 1; i < argc; ++i) {
00185 if (!isdialstr(argv[i])) continue;
00186 tell(commfd, argv[i] + 1, "\r", NIL);
00187 candial = 1;
00188 }
00189
00190
00191 while (read(0, &key, 1) == 1) {
00192 if (key == HOTKEY) {
00193
00194 if (read(0, &key, 1) != 1) continue;
00195
00196 switch (key) {
00197 default:
00198
00199 for (i = 1; i < argc; ++i) {
00200 char *arg = argv[i];
00201
00202 if (arg[0] == '-' && arg[1] == 'c'
00203 && arg[2] == key) {
00204 reader(0);
00205 tcsetattr(0, TCSANOW, &tcsavestdin);
00206 shell(arg+3);
00207 tcsetattr(0, TCSANOW, &tcstdin);
00208 reader(1);
00209 break;
00210 }
00211 }
00212 if (i < argc) break;
00213
00214
00215 tell(1, "\r\nTerm commands:\r\n",
00216 " ? - this help\r\n",
00217 candial ? " d - redial\r\n" : "",
00218 " s - subshell (e.g. for file transfer)\r\n",
00219 " h - hangup (+++ ATH)\r\n",
00220 " b - send a break\r\n",
00221 " q - exit term\r\n",
00222 NIL);
00223 for (i = 1; i < argc; ++i) {
00224 char *arg = argv[i];
00225 static char cmd[] = " x - ";
00226
00227 if (arg[0] == '-' && arg[1] == 'c'
00228 && arg[2] != 0) {
00229 cmd[1] = arg[2];
00230 tell(1, cmd, arg+3, "\r\n", NIL);
00231 }
00232 }
00233 tell(1, "^] - send a CTRL-]\r\n\n",
00234 NIL);
00235 break;
00236 case 'd':
00237
00238 for (i = 1; i < argc; ++i) {
00239 if (!isdialstr(argv[i])) continue;
00240 tell(commfd, argv[i] + 1, "\r", NIL);
00241 }
00242 break;
00243 case 's':
00244
00245 reader(0);
00246 tcsetattr(0, TCSANOW, &tcsavestdin);
00247 shell(NULL);
00248 tcsetattr(0, TCSANOW, &tcstdin);
00249 reader(1);
00250 break;
00251 case 'h':
00252
00253 sleep(2);
00254 tell(commfd, "+++", NIL);
00255 sleep(2);
00256 tell(commfd, "ATH\r", NIL);
00257 break;
00258 case 'b':
00259
00260 tcsendbreak(commfd, 0);
00261 break;
00262 case 'q':
00263
00264 quit(0);
00265 case HOTKEY:
00266 (void) write(commfd, &key, 1);
00267 break;
00268 }
00269 } else {
00270
00271 if (write(commfd, &key, 1) != 1) break;
00272 }
00273 }
00274 tell(2, "term: nothing to copy from input to ", commdev, "?\r\n", NIL);
00275 quit(1);
00276 }
00277
00278
00279 int isdialstr(char *arg)
00280 {
00281
00282
00283 return (arg[0] == '-'
00284 && (arg[1] == 'a' || arg[1] == 'A')
00285 && (arg[2] == 't' || arg[2] == 'T'));
00286 }
00287
00288
00289 void tell(int fd, ...)
00290 {
00291
00292 va_list ap;
00293 char *s;
00294
00295 va_start(ap, fd);
00296 while ((s = va_arg(ap, char *)) != NIL) write(fd, s, strlen(s));
00297 va_end(ap);
00298 }
00299
00300
00301 void reader(on)
00302 int on;
00303 {
00304
00305
00306 static pid_t pid;
00307 char buf[CHUNK];
00308 ssize_t n, m, r;
00309
00310 if (!on) {
00311
00312 if (pid == 0) return;
00313 kill(pid, SIGKILL);
00314 (void) waitpid(pid, (int *) NULL, 0);
00315 pid = 0;
00316 return;
00317 }
00318
00319
00320 pid = fork();
00321 if (pid < 0) {
00322 tell(2, "term: fork() failed: ", strerror(errno), "\r\n", NIL);
00323 quit(1);
00324 }
00325 if (pid == 0) {
00326
00327
00328 while ((n = read(commfd, buf, sizeof(buf))) > 0) {
00329 m = 0;
00330 while (m < n && (r = write(1, buf + m, n - m)) > 0) m += r;
00331 }
00332 tell(2, "term: nothing to copy from ", commdev, " to output?\r\n", NIL);
00333 kill(getppid(), SIGTERM);
00334 _exit(1);
00335 }
00336
00337 }
00338
00339
00340 void shell(char *cmd)
00341 {
00342
00343
00344
00345
00346 pid_t pid;
00347 char *shell, *sh0;
00348 _PROTOTYPE(void (*isav), (int));
00349 _PROTOTYPE(void (*qsav), (int));
00350 _PROTOTYPE(void (*tsav), (int));
00351
00352 if (cmd == NULL) {
00353 tell(1, "\nExit the shell to return to term, ",
00354 commdev, " is open on file descriptor 9.\n", NIL);
00355 }
00356
00357 if (cmd != NULL || (shell = getenv("SHELL")) == NULL) shell = "/bin/sh";
00358 if ((sh0 = strrchr(shell, '/')) == NULL) sh0 = shell; else sh0++;
00359
00360
00361 pid = fork();
00362 if (pid < 0) {
00363 tell(2, "term: fork() failed: ", strerror(errno), "\n", NIL);
00364 return;
00365 }
00366 if (pid == 0) {
00367
00368 setgid(getgid());
00369 setuid(getuid());
00370
00371 if (commfd != 9) { dup2(commfd, 9); close(commfd); }
00372
00373 if (cmd == NULL) {
00374 execl(shell, sh0, (char *) NULL);
00375 } else {
00376 execl(shell, sh0, "-c", cmd, (char *) NULL);
00377 }
00378 tell(2, "term: can't execute ", shell, ": ", strerror(errno), "\n",NIL);
00379 _exit(1);
00380 }
00381
00382 isav = signal(SIGINT, SIG_IGN);
00383 qsav = signal(SIGQUIT, SIG_IGN);
00384 tsav = signal(SIGTERM, SIG_IGN);
00385 (void) waitpid(pid, (int *) 0, 0);
00386 (void) signal(SIGINT, isav);
00387 (void) signal(SIGQUIT, qsav);
00388 (void) signal(SIGTERM, tsav);
00389 tell(1, "\n[back to term]\n", NIL);
00390 }
00391
00392
00393 void lock_device(device)
00394 char *device;
00395 {
00396
00397
00398 struct stat stbuf;
00399 unsigned int pid;
00400 int fd;
00401 int n;
00402 int u;
00403
00404 if (stat(device, &stbuf) < 0) fatal(device);
00405
00406 if (!S_ISCHR(stbuf.st_mode)) {
00407 tell(2, "term: ", device, " is not a character device\n", NIL);
00408 exit(1);
00409 }
00410
00411
00412 setnum(lockfile + 23, (stbuf.st_dev >> 8) & 0xFF);
00413 setnum(lockfile + 27, (stbuf.st_rdev >> 8) & 0xFF);
00414 setnum(lockfile + 31, (stbuf.st_rdev >> 0) & 0xFF);
00415
00416
00417 u = umask(0);
00418 for (;;) {
00419 if ((fd = open(lockfile, O_RDONLY)) < 0) {
00420
00421 if (errno != ENOENT) fatal(device);
00422 if ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0444)) < 0) {
00423 if (errno == EEXIST) continue;
00424 fatal(lockfile);
00425 }
00426 pid = getpid();
00427 n = write(fd, &pid, sizeof(pid));
00428 if (n < 0) {
00429 n = errno;
00430 (void) unlink(lockfile);
00431 errno = n;
00432 fatal(lockfile);
00433 }
00434 close(fd);
00435 break;
00436 } else {
00437
00438 n = read(fd, &pid, sizeof(pid));
00439 if (n < 0) fatal(device);
00440 close(fd);
00441 if (n == sizeof(pid) && !(kill(pid, 0) < 0 && errno == ESRCH)) {
00442
00443 tell(2, "term: ", device,
00444 " is in use by another program\n", NIL);
00445 if (getpgrp() == getpid()) sleep(3);
00446 exit(1);
00447 }
00448
00449 tell(1, "Removing stale lock ", lockfile, "\n", NIL);
00450 if (unlink(lockfile) < 0 && errno != ENOENT) fatal(lockfile);
00451 }
00452 }
00453
00454
00455
00456 umask(u);
00457 }
00458
00459
00460 void fatal(char *label)
00461 {
00462 tell(2, "term: ", label, ": ", strerror(errno), "\n", NIL);
00463 exit(1);
00464 }
00465
00466
00467 void setnum(char *s, int n)
00468 {
00469
00470 int i;
00471
00472 for (i = 0; i < 3; i++) { *--s = '0' + (n % 10); n /= 10; }
00473 }
00474
00475
00476 void set_uart(argc, argv, tcp)
00477 int argc;
00478 char *argv[];
00479 struct termios *tcp;
00480 {
00481
00482
00483 int i;
00484 char *arg;
00485 struct param_s *param;
00486
00487
00488 for (i = 1; i < argc; ++i) {
00489 arg = argv[i];
00490 if (arg[0] == '/' || arg[0] == '-') continue;
00491
00492
00493 for (param = ¶ms[0];
00494 param->type != BAD && strcmp(arg, param->pattern) != 0;
00495 ++param);
00496 switch (param->type) {
00497 case BAD:
00498 tell(2, "Invalid parameter: ", arg, "\n", NIL);
00499 quit(1);
00500 break;
00501 case BITS:
00502 tcp->c_cflag &= ~CSIZE;
00503 tcp->c_cflag |= param->value;
00504 break;
00505 case PARITY:
00506 tcp->c_cflag &= PARENB | PARODD;
00507 tcp->c_cflag |= param->value;
00508 break;
00509 case SPEED:
00510 cfsetispeed(tcp, (speed_t) param->value);
00511 cfsetospeed(tcp, (speed_t) param->value);
00512 break;
00513 }
00514 }
00515 }
00516
00517
00518 void set_raw(tcp)
00519 struct termios *tcp;
00520 {
00521
00522
00523 tcp->c_iflag &= ~(ICRNL|IGNCR|INLCR|IXON|IXOFF);
00524 tcp->c_lflag &= ~(ICANON|IEXTEN|ISIG|ECHO|ECHONL);
00525 tcp->c_oflag &= ~(OPOST);
00526 tcp->c_cc[VMIN] = 1;
00527 tcp->c_cc[VTIME] = 0;
00528 }
00529
00530
00531 void quit(code)
00532 int code;
00533 {
00534
00535 reader(0);
00536 tcsetattr(0, TCSANOW, &tcsavestdin);
00537 (void) unlink(lockfile);
00538 exit(code);
00539 }