home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / pcomm-2.0.2 / part05 / port.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-13  |  13.1 KB  |  552 lines

  1. /*
  2.  * Routines to get or release a TTY port.
  3.  */
  4.  
  5. #define MAX_PID    30000
  6. #define TRUE    1
  7. #define FALSE    0
  8.  
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include "config.h"
  12. #include "dial_dir.h"
  13. #include "modem.h"
  14. #include "status.h"
  15.  
  16. #ifdef BSD
  17. #include <sys/file.h>
  18. #else /* BSD */
  19. #include <fcntl.h>
  20. #endif /* BSD */
  21.  
  22. #ifdef UNIXPC
  23. #include <sys/phone.h>
  24. #endif /* UNIXPC */
  25.  
  26. #ifdef XENIX_LOCKS
  27. #include <ctype.h>
  28. #endif /* XENIX_LOCKS */
  29.  
  30. #ifdef SVR4_LOCKS
  31. #include <sys/types.h>
  32. #include <sys/stat.h>
  33. #include <sys/mkdev.h>
  34. #endif /* SVR4_LOCKS */
  35.  
  36. static int getty_status = 0;
  37. static char *lock_path = NULL;
  38. static int set_getty(), checklock(), ck_speed();
  39.  
  40. /*
  41.  * Finds a free (or requested) serial port.  Creates a lock file to hold
  42.  * for our use.  Loads the modem database.  A non-zero return code means
  43.  * all ports (or the requested port) are busy.
  44.  */
  45.  
  46. int
  47. get_port()
  48. {
  49.     extern int fd, errno;
  50.     register int i;
  51.     int j, k, lfd, list[NUM_TTY], cmask, aux,  progpid, keep_it, trash_it;
  52.     char file[80], buf[80], message[80], *str_rep();
  53.     void error_win(), line_set(), release_port(), send_str(), ipc_update();
  54. #ifdef XENIX_LOCKS
  55.     char *last_c;
  56. #endif /* XENIX_LOCKS */
  57.  
  58.     aux = chk_aux(dir->aux[dir->d_cur]);
  59.     /*
  60.      * If you already have a port, see if it is good enough for the
  61.      * current request.
  62.      */
  63.     if (fd != -1) {
  64.         keep_it = 0;
  65.         trash_it = 0;
  66.                     /* got the requested TTY ? */
  67.         if (aux == IS_TTY) {
  68.             if (!strcmp(dir->aux[dir->d_cur], modem->tty[modem->t_cur]))
  69.                 keep_it++;
  70.             else
  71.                 trash_it++;
  72.         }
  73.                     /* got the requested Modem? */
  74.         if (aux == IS_MODEM) {
  75.             if (!strcmp(dir->aux[dir->d_cur], modem->tname[modem->t_cur]))
  76.                 keep_it++;
  77.             else
  78.                 trash_it++;
  79.         }
  80.                     /* can it handle the request baud? */
  81.         if (ck_speed(modem->t_cur, dir->baud[dir->d_cur]))
  82.             keep_it++;
  83.  
  84.         if (keep_it && !trash_it) {
  85.             /*
  86.              * Reset the line parameters, because baud rate,
  87.              * etc. may have changed.
  88.              */
  89.             if (dir->d_cur != 0) {
  90.                 dir->baud[0] = dir->baud[dir->d_cur];
  91.                 dir->parity[0] = dir->parity[dir->d_cur];
  92.                 dir->data_bits[0] = dir->data_bits[dir->d_cur];
  93.                 dir->stop_bits[0] = dir->stop_bits[dir->d_cur];
  94.                 dir->duplex[0] = dir->duplex[dir->d_cur];
  95.                 dir->aux[0] = str_rep(dir->aux[0], dir->aux[dir->d_cur]);
  96.             }
  97.             line_set();
  98.             return(0);
  99.         }
  100.     }
  101.  
  102.     /*
  103.      * If we have a script running, we can't swap TTY ports, because
  104.      * we've already forked the child process with "fd" as an open
  105.      * file descriptor.  Changing "fd" in the parent won't change the
  106.      * "fd" in the child.
  107.      */
  108.     if (fd != -1 && status->dup_fd != -1) {
  109.         error_win(0, "Can't change TTYs while a script is being played", "dial aborted");
  110.         return(1);
  111.     }
  112.  
  113.     release_port(VERBOSE);
  114.  
  115.     list[0] = -1;
  116.     /*
  117.      * See if you want a specific TTY port.  If the auxiliary field in
  118.      * the dialing directory is a valid device name, then use that TTY.
  119.      */
  120.     if (aux == IS_TTY) {
  121.         for (i=0; i<modem->t_entries; i++) {
  122.                     /* and it exists in modem database */
  123.             if (!strcmp(dir->aux[dir->d_cur], modem->tty[i])) {
  124.                 list[0] = i;
  125.                 list[1] = -1;
  126.                 break;
  127.             }
  128.         }
  129.                     /* oops... we don't know that port */
  130.         if (list[0] == -1) {
  131.             sprintf(message, "Device \"%s\" in the auxiliary field doesn't exist in", dir->aux[dir->d_cur]);
  132.             sprintf(buf, "modem/TTY database \"%s\"", modem->m_path);
  133.             error_win(0, message, buf);
  134.             return(1);
  135.         }
  136.     }
  137.  
  138.     /*
  139.      * If we haven't specified a port, create a list of acceptable TTYs.
  140.      * It searches the modem database for the requested baud rate.
  141.      */
  142.     k = 0;
  143.     if (list[0] == -1) {
  144.         for (i=0; i<modem->t_entries; i++) {
  145.                     /* skip ports with no modems */
  146.             if (!strcmp(modem->tname[i], "DIRECT"))
  147.                 continue;
  148.  
  149.                     /* weed out the modems we don't want */
  150.             if (aux == IS_MODEM) {
  151.                 if (strcmp(modem->tname[i], dir->aux[dir->d_cur]))
  152.                     continue;
  153.             }
  154.                     /* can handle requested baud rate? */
  155.             if (ck_speed(i, dir->baud[dir->d_cur]))
  156.                 list[k++] = i;
  157.         }
  158.                     /* the end of list marker */
  159.         list[k] = -1;
  160.     }
  161.                     /* empty list? */
  162.     if (list[0] == -1) {
  163.         if (aux == IS_MODEM) {
  164.             sprintf(message, "The requested modem \"%s\" does not exists", dir->aux[dir->d_cur]);
  165.             sprintf(buf, "or does not support the requested baud rate %d", dir->baud[dir->d_cur]);
  166.         }
  167.         else {
  168.             sprintf(message, "No modem at a %d baud rating exists in the", dir->baud[dir->d_cur]);
  169.             sprintf(buf, "modem database \"%s\"", modem->m_path);
  170.         }
  171.         error_win(0, message, buf);
  172.         return(1);
  173.     }
  174.                     /* check the list for a free port */
  175.     i = 0;
  176.     while (list[i] != -1) {
  177.                     /* create a lock file name */
  178. #ifdef SVR4_LOCKS
  179.         struct stat sbuf;
  180.         sprintf(buf, "/dev/%s", modem->tty[list[i]]);
  181.         stat(buf, &sbuf);
  182.         sprintf(file, "%s/LK.%03d.%03d.%03d", LOCK_DIR, major(sbuf.st_dev), major(sbuf.st_rdev), minor(sbuf.st_rdev));
  183. #else /* SVR4_LOCKS */
  184.         sprintf(file, "%s/LCK..%s", LOCK_DIR, modem->tty[list[i]]);
  185. #endif /* SVR4_LOCKS */
  186.  
  187. #ifdef XENIX_LOCKS
  188.         last_c = file + strlen(file)-1;
  189.         if (isupper(*last_c))
  190.             *last_c = tolower(*last_c);
  191. #endif /* XENIX_LOCKS */
  192.  
  193. #ifdef DEBUG
  194.         fprintf(stderr, "get_port: checking '/dev/%s'\n", modem->tty[list[i]]);
  195. #endif /* DEBUG */
  196.  
  197.                     /* no lock exists or it is dead */
  198.         if (checklock(file)) {
  199.             getty_status = set_getty(modem->tty[list[i]], FALSE);
  200.  
  201.             cmask = umask(0);
  202.             if ((lfd = open(file, O_CREAT|O_EXCL|O_WRONLY, 0666)) < 0) {
  203.                 if (getty_status)
  204.                     set_getty(modem->tty[list[i]], TRUE);
  205.                 sprintf(buf, "\"%s\"", file);
  206.                 error_win(0, "Can't create the lockfile", buf);
  207.                 return(1);
  208.             }
  209.             umask(cmask);
  210.             progpid = getpid();
  211. #ifdef ASCII_PID
  212.             sprintf(buf, "%10d\n", progpid);
  213.             write(lfd, buf, 11);
  214. #else /* ASCII_PID */
  215.             write(lfd, (char *) &progpid, sizeof(int));
  216. #endif /* ASCII_PID */
  217.             close(lfd);
  218.                     /* store the new values */
  219.             lock_path = str_rep(lock_path, file);
  220.             modem->t_cur = list[i];
  221.  
  222.                     /* load the modem data base */
  223.             modem->m_cur = -1;
  224.             for (j=0; j<modem->m_entries; j++) {
  225.                 if (!strcmp(modem->tname[modem->t_cur], modem->mname[j])) {
  226.                     modem->m_cur = j;
  227.                     break;
  228.                 }
  229.             }
  230.             if (modem->m_cur == -1) {
  231.                 sprintf(buf, "Modem name \"%s\" in TTY database",
  232.                  modem->tname[modem->t_cur]);
  233.                 error_win(0, buf, "does not exist in modem database");
  234.                 modem->t_cur = -1;
  235.                 return(1);
  236.             }
  237.  
  238.                     /* open the device (ignore DCD) */
  239.             sprintf(buf, "/dev/%s", modem->tty[modem->t_cur]);
  240.             if ((fd = open(buf, O_RDWR|O_NDELAY)) < 0) {
  241.                 if (getty_status)
  242.                     set_getty(modem->tty[modem->t_cur], TRUE);
  243.                 /*
  244.                  * For systems that use flock() instead of
  245.                  * UUCP lock files
  246.                  */
  247.                 if (errno == EBUSY) {
  248.                     unlink(lock_path);
  249.                     modem->m_cur = -1;
  250.                     modem->t_cur = -1;
  251.                     i++;
  252.                     continue;
  253.                 }
  254.                 sprintf(file, "Can't open port \"%s\" for read and write", buf);
  255.                 error_win(0, file, "");
  256.                 modem->m_cur = -1;
  257.                 modem->t_cur = -1;
  258.                 return(1);
  259.             }
  260.                     /* change line settings */
  261.             if (dir->d_cur != 0) {
  262.                 dir->baud[0] = dir->baud[dir->d_cur];
  263.                 dir->parity[0] = dir->parity[dir->d_cur];
  264.                 dir->data_bits[0] = dir->data_bits[dir->d_cur];
  265.                 dir->stop_bits[0] = dir->stop_bits[dir->d_cur];
  266.                 dir->duplex[0] = dir->duplex[dir->d_cur];
  267.                 dir->aux[0] = str_rep(dir->aux[0], dir->aux[dir->d_cur]);
  268.             }
  269.             line_set();
  270.  
  271.                     /* turn off the O_NDELAY setting */
  272.             tty_noblock(fd, FALSE);
  273.  
  274.             /*
  275.              * For some reason, the following witchcraft is
  276.              * often required.
  277.              */
  278. #ifdef O_NDELAY_BROKE
  279.             close(open(buf, O_RDWR));
  280. #endif /* O_NDELAY_BROKE */
  281.  
  282.                     /* initialize the modem */
  283.             send_str(modem->init[modem->m_cur], SLOW);
  284.                     /* update the poll()/select() values */
  285.             ipc_update(fd, status->cmd_ipc);
  286.             return(0);
  287.         }
  288.         i++;
  289.     }
  290.     if (aux == IS_TTY) {
  291.         sprintf(buf, "The requested port \"%s\", is busy", modem->tty[modem->t_cur]);
  292.         error_win(0, buf, "Try again later");
  293.     }
  294.     else
  295.         error_win(0, "All ports are busy now, try again later", "");
  296.     return(1);
  297. }
  298.  
  299. /*
  300.  * Release the port.  Closes the file descriptor and removes the
  301.  * lock file
  302.  */
  303.  
  304. void
  305. release_port(verbose)
  306. int verbose;
  307. {
  308.     extern int fd;
  309.     extern char *null_ptr;
  310.     char buf[80];
  311.     void free_ptr(), hang_up(), reset_line(), error_win(), ipc_update();
  312.  
  313.     /*
  314.      * The modem structure can't be guaranteed to exist yet.  For example,
  315.      * an error in reading one of the other support files would cause
  316.      * this routine to be used before the MODEM structure gets allocated.
  317.      */
  318.     if (modem == NULL)
  319.         return;
  320.                     /* close the port */
  321.     if (fd != -1) {
  322.         tty_flush(fd, 2);
  323.         /*
  324.          * Since HUPCL is set, the close() should drop the DTR and
  325.          * hang up the modem (provided you've got the modem to
  326.          * respond to DTR).  Since this is not guaranteed, we send
  327.          * the hang_up string first.
  328.          */
  329.         hang_up(verbose);
  330.         reset_line();
  331.         close(fd);
  332.     }
  333.                     /* remove the lock */
  334.     if (lock_path != NULL && *lock_path != '\0') {
  335.         if (unlink(lock_path)) {
  336.             sprintf(buf, "\"%s\"", lock_path);
  337.             error_win(0, "Can't remove the lock file", buf);
  338.         }
  339.         free_ptr(lock_path);
  340.         lock_path = null_ptr;
  341.     }
  342.                     /* turn the getty back on? */
  343.     if (getty_status && modem->t_cur != -1)
  344.         set_getty(modem->tty[modem->t_cur], TRUE);
  345.                     /* clean up the structure */
  346.     fd = -1;
  347.     modem->m_cur = -1;
  348.     modem->t_cur = -1;
  349.     ipc_update(fd, status->cmd_ipc);
  350.     return;
  351. }
  352.  
  353. /*
  354.  * Turn the /etc/getty on or off for the specified port.  A non-zero return
  355.  * code means that the getty was on.  Systems with uugetty() or dedicated
  356.  * dialout ports won't need this routine.
  357.  */
  358.  
  359. /* ARGSUSED */
  360. static int
  361. set_getty(tty, on)
  362. char *tty;
  363. int on;
  364. {
  365. #ifdef UNIXPC
  366.     int i, ret_code;
  367.     char buf[40];
  368.     unsigned int sleep();
  369.                     /* the last three characters */
  370.     i = strlen(tty) -3;
  371.  
  372.     ret_code = 0;
  373.     if (on) {
  374.         sprintf(buf, "setgetty %s 1", tty+i);
  375.         system(buf);
  376.     }
  377.     else {
  378.         sprintf(buf, "setgetty %s 0", tty+i);
  379.         if (system(buf) == 512)
  380.             ret_code++;
  381.         sleep(1);
  382.     }
  383.     return(ret_code);
  384. #else /* UNIXPC */
  385.     /*
  386.      * If you don't have one of these cute little routines, you
  387.      * might wanna write one.  It should check for an existing lock
  388.      * file, edit the /etc/inittab file, and issue an init -q.
  389.      * The return code should tell if there was a getty or not.
  390.      * Obviously the program would be suid to root.
  391.      */
  392.     return(0);
  393. #endif /* UNIXPC */
  394. }
  395.  
  396. /*
  397.  * Check the lock file for a valid pid value.  Error conditions such
  398.  * as not being able to open the lock file or not being able to interpret
  399.  * the contents of the lock file cause the code to assume that the lock
  400.  * file is valid.  Let the user worry about weird special cases.  A 
  401.  * non-zero return code means the lock is dead or doesn't exist.
  402.  */
  403.  
  404. static int
  405. checklock(lockfile)
  406. char *lockfile;
  407. {
  408.     extern int errno;
  409.     int lfd, lckpid;
  410.     unsigned int sleep();
  411. #ifdef ASCII_PID
  412.     int n;
  413.     char buf[40];
  414. #endif /* ASCII_PID */
  415.                     /* doesn't exist */
  416.     if (access(lockfile, 0))
  417.         return(1);
  418.                     /* can't open the lock file */
  419.     if ((lfd = open(lockfile, 0)) < 0)
  420.         return(0);
  421.  
  422. #ifdef ASCII_PID
  423.     if ((n = read(lfd, buf, 40)) <= 0) {
  424.         close(lfd);
  425.         return(0);
  426.     }
  427.     close(lfd);
  428.     buf[n--] = '\0';
  429.     lckpid = atoi(buf);
  430. #else /* ASCII_PID */
  431.     if (read(lfd, (char *) &lckpid, sizeof(int)) != sizeof(int)) {
  432.         close(lfd);
  433.         return(0);
  434.     }
  435.     close(lfd);
  436. #endif /* ASCII_PID */
  437.                     /* invalid pid? */
  438.     if (lckpid <= 0 || lckpid > MAX_PID)
  439.         return(0);
  440.  
  441.     if ((kill(lckpid, 0) == -1) && (errno == ESRCH)) {
  442.         /*
  443.          * If the kill was unsuccessful due to an ESRCH error,
  444.          * that means the process is no longer active and the
  445.          * lock file can be safely removed.
  446.          */
  447.         unlink(lockfile);
  448.         sleep(1);
  449.         return(1);
  450.     }
  451.     /*
  452.      * If the kill() was successful, that means the process must
  453.      * still be active.
  454.      */
  455.     return(0);
  456. }
  457.  
  458. /*
  459.  * Check to see if the desired baud rate can be handled by the modem.
  460.  * Uses the connect strings to make this determination.  The first
  461.  * argument is the index into the TTY database.  A non-zero return code
  462.  * means "yes it can".
  463.  */
  464.  
  465. static int
  466. ck_speed(tty, baud)
  467. int tty;
  468. unsigned int baud;
  469. {
  470.     int i, mod;
  471.     char buf[60];
  472.     void error_win();
  473.                     /* find the modem database */
  474.     mod = -1;
  475.     for (i=0; i<modem->m_entries; i++) {
  476.         if (!strcmp(modem->mname[i], modem->tname[tty])) {
  477.             mod = i;
  478.             break;
  479.         }
  480.     }
  481.     if (mod == -1) {
  482.         sprintf(buf, "Modem name \"%s\" in TTY database", modem->tname[tty]);
  483.         error_win(1, buf, "does not exist in modem database");
  484.     }
  485.  
  486. #ifdef DEBUG
  487.     fprintf(stderr, "ck_speed: checking modem \"%s\" for %d baud\n", modem->mname[mod], baud);
  488. #endif /* DEBUG */
  489.  
  490.     switch (baud) {
  491.         case 300:
  492.             if (*modem->con_3[mod] != '\0')
  493.                 return(1);
  494.             break;
  495.         case 1200:
  496.             if (*modem->con_12[mod] != '\0')
  497.                 return(1);
  498.             break;
  499.         case 2400:
  500.             if (*modem->con_24[mod] != '\0')
  501.                 return(1);
  502.             break;
  503.         case 4800:
  504.             if (*modem->con_48[mod] != '\0')
  505.                 return(1);
  506.             break;
  507.         case 7200:
  508.         case 9600:
  509.             if (*modem->con_96[mod] != '\0')
  510.                 return(1);
  511.             break;
  512.         case 12000:
  513.         case 14400:
  514.         case 19200:
  515.             if (*modem->con_192[mod] != '\0')
  516.                 return(1);
  517.             break;
  518.         case 38400:
  519.             if (*modem->con_384[mod] != '\0')
  520.                 return(1);
  521.             break;
  522.     }
  523.     return(0);
  524. }
  525.  
  526. /*
  527.  * Check to see if the auxiliary field contains a valid TTY or modem name.
  528.  */
  529.  
  530. chk_aux(aux)
  531. char *aux;
  532. {
  533.     int i;
  534.     char buf[80], *strcpy(), *strcat();
  535.  
  536.     if (*aux == '\0')
  537.         return(0);
  538.  
  539.     strcpy(buf, "/dev/");
  540.     strcat(buf, aux);
  541.  
  542.     if (!access(buf, 0))
  543.         return(IS_TTY);
  544.  
  545.     for (i=0; i<modem->m_entries; i++) {
  546.         if (!strcmp(modem->mname[i], aux))
  547.             return(IS_MODEM);
  548.     }
  549.                       /* Well, is not a TTY or modem */
  550.     return(IS_SCRIPT);
  551. }
  552.