home *** CD-ROM | disk | FTP | other *** search
/ Encyclopedia of Graphics File Formats Companion / GFF_CD.ISO / software / unix / saoimage / sao1_07.tar / vms / zfiovi.c < prev   
C/C++ Source or Header  |  1990-05-12  |  15KB  |  690 lines

  1. /*
  2.  * ZFIOVI.C -- VMS Imtool driver.  These interface routines are used by the
  3.  *    interactive binary graphics interface (ZFIOGD) and any image display
  4.  *    programs that support the Imtool protocol.
  5.  *
  6.  *    Interface routines:
  7.  *
  8.  *        zopnvi        Open channel for VMS/IRAF-Imtool communication.
  9.  *        zclsvi        Close channel.
  10.  *        zardvi        Read from channel.
  11.  *        zawrvi        Write to channel.
  12.  *        zawtvi        Wait on read/write completion.
  13.  *
  14.  *    Supplementary interface routines (not used by VMS/IRAF ZFIOGD):
  15.  *
  16.  *        ZBufSize        Returns maximum i/o transfer size.
  17.  *        ZPending        Check if data is waiting to be read.
  18.  *        ZSelectAsyncInput    Allow asynchronous i/o notification.
  19.  *
  20.  * NOTES
  21.  *
  22.  *    For this prototype implementation, VMS mailboxes are used.  A limit
  23.  *    of 8192 bytes per i/o operation is assumed by ZFIOGD and any image
  24.  *    display program that uses these routines (e.g., SAOimage).  The i/o
  25.  *    is effectively synchronous given the behavior of VMS mailboxes.
  26.  *
  27.  *    The supplementary interface routines are to support image display
  28.  *    programs which need to wait on window events and Imtool "pipe" i/o
  29.  *    simultaneously a la Unix select().  These functions are modelled
  30.  *    after similar ones in Xwindows (which may be VMS-dependent also).
  31.  *
  32.  *    Since this will be linked into VMS/IRAF applications, the standard
  33.  *    C library functions are not used.  Only VMS/IRAF-supported ones
  34.  *    and VMS system routines are referenced here.
  35.  *
  36.  *    The mailbox device names are passed between the cooperating
  37.  *    processes via locks.  This somewhat sneaky approach is used
  38.  *    so that GRPNAM privilege is not required for VMS/IRAF users.
  39.  *    This approach assumes that mailbox device names (_MBAnnnn:)
  40.  *    will fit into the 16-byte lock value block, which is not a
  41.  *    problem as of VMS V5.2.
  42.  *
  43.  * HISTORY
  44.  *
  45.  *    Oct-1989   Jay Travisano   Space Telescope Science Institute
  46.  *        Prototype implementation to support SAOimage with VMS/IRAF.
  47.  */
  48.  
  49. #include <descrip.h>    /* Descriptor definitions        */
  50. #include <dvidef.h>    /* Get device info definitions        */
  51. #include <jpidef.h>    /* Get job/process info definitions    */
  52. #include <lckdef.h>    /* Lock definitions            */
  53. #include <iodef.h>    /* I/O definitions            */
  54. #include <ssdef.h>    /* System status definitions        */
  55. #include <syidef.h>    /* Get system info definitions        */
  56.  
  57. #ifdef IMTOOL
  58. #define import_spp    /* Import the IRAF SPP constants    */
  59. #include <iraf.h>
  60. #else
  61. #define READ_ONLY    1    /* file access modes */
  62. #define XERR        (-1)
  63. #define XOK        0
  64. #define XINT        int
  65. #define XLONG        long
  66. #endif
  67.  
  68. #define SZ_MBX        8192    /* maximum transfer size    */
  69. #define SZ_MBXNAME    48    /* size of logical mailbox name */
  70.  
  71. #define SZ_USERNAME    12    /* VMS size limits        */
  72. #define SZ_NODENAME     15
  73. #define SZ_LOCKNAME    31
  74. #define SZ_LOCKVALBLK    16
  75.  
  76. #define VMS_OKAY(s)    (s & 1)    /* VMS status code checking    */
  77. #define VMS_ERROR(s) (!(s & 1))
  78.  
  79.  
  80. #define SET_DESCRIPTOR(name,value,length) \
  81.         name.dsc$a_pointer = value; \
  82.         name.dsc$w_length  = length; \
  83.         name.dsc$b_dtype   = DSC$K_DTYPE_T; \
  84.         name.dsc$b_class   = DSC$K_CLASS_S
  85.  
  86. typedef  struct dsc$descriptor_s  DESC;
  87.  
  88.  
  89. struct io_status_block {
  90.     short    status;
  91.     short    count;
  92.     long    pid;
  93. };
  94. typedef  struct io_status_block  IOSB;
  95.  
  96.  
  97. struct lock_status_block {
  98.     short    status;
  99.     short    reserved;
  100.     long    lkid;
  101.     char    valblk[SZ_LOCKVALBLK];
  102. };
  103. typedef  struct lock_status_block  LOCK;
  104.  
  105.  
  106. struct channel_info {
  107.     short    mbx_chan;
  108.     IOSB    iosb;
  109.     int    lockid;
  110.     int    qio_status;
  111.     int    io_pending;
  112.     int    (*ast)();
  113.     int    ast_arg;
  114. };
  115. typedef  struct channel_info  CHANNEL;
  116.  
  117.  
  118. #define NUM_CHANNELS  2
  119.  
  120. static  CHANNEL  channels[NUM_CHANNELS];
  121.  
  122.  
  123. /*
  124.  * ZOPNVI -- Open an IRAF-Imtool communication path using mailboxes.
  125.  */
  126.  
  127. zopnvi (imtool_name, mode, chan)
  128. char    *imtool_name;
  129. XINT    *mode;
  130. XINT    *chan;
  131. {
  132.     char    username[SZ_USERNAME+1];
  133.     DESC    _username;
  134.  
  135.     char    mbxname[SZ_MBXNAME+1];
  136.     DESC    _mbxname;
  137.  
  138.     char    devname[SZ_LOCKVALBLK];
  139.     DESC    _devname;
  140.  
  141.     short    len, ch;
  142.     int    stat, lockid, i;
  143.  
  144.     /* Generate mailbox logical name from Imtool name and username.
  145.      */
  146.  
  147.     SET_DESCRIPTOR (_username, username, sizeof(username)-1);
  148.  
  149.     lib$getjpi (&JPI$_USERNAME, 0, 0, 0, &_username, &len);
  150.     username[len] = 0;
  151.     while (username[--len] == ' ')        /* trim trailing blanks */
  152.         username[len] = 0;
  153.  
  154.     strcat (strcat (strcpy (mbxname, imtool_name), "_"), username);
  155.  
  156.     /* Open or create the mailbox.  First, enqueue a lock using the
  157.      * mailbox logical name just formatted.  If the mailbox already
  158.      * exists, the lock value returned will contain the device name.
  159.      * If not, we'll have to create the mailbox and update the lock
  160.      * with the actual device name.  Dequeue the lock on any error.
  161.      */
  162.  
  163.     if ((lockid = Enqueue_Lock (mbxname, devname)) == 0) {
  164.         *chan = XERR;
  165.         return;
  166.     }
  167.  
  168.     if (devname[0] && strncmp (devname, "_MBA", 4) == 0) {
  169.  
  170.         /* Mailbox already exists; assign a channel to it.
  171.          */
  172.  
  173.         SET_DESCRIPTOR (_devname, devname, strlen(devname));
  174.  
  175.         stat = sys$assign (&_devname, &ch, 0, 0);
  176.  
  177.         if VMS_ERROR(stat) {
  178.         Dequeue_Lock (lockid);
  179.         putmsg (devname, 0);
  180.         putmsg ("ZFIOVI: Mailbox assign error", stat);
  181.         *chan = XERR;
  182.         return;
  183.         }
  184.  
  185.         Update_Lock (lockid, 0);
  186.  
  187.     } else {
  188.         /* Create the mailbox and update the lock with the
  189.          * actual device name.
  190.          */
  191.  
  192.         int  bufquo;
  193.  
  194.         if (*mode == READ_ONLY)
  195.         bufquo = SZ_MBX * 2;
  196.         else
  197.         bufquo = SZ_MBX;
  198.  
  199.         SET_DESCRIPTOR (_mbxname, mbxname, strlen(mbxname));
  200.  
  201.         stat = sys$crembx (0, &ch, SZ_MBX, bufquo, 0, 0, &_mbxname);
  202.  
  203.         if VMS_ERROR(stat) {
  204.         Dequeue_Lock (lockid);
  205.         putmsg (mbxname, 0);
  206.         putmsg ("ZFIOVI: Mailbox creation error", stat);
  207.         *chan = XERR;
  208.         return;
  209.         }
  210.  
  211.         SET_DESCRIPTOR (_devname, devname, sizeof(devname));
  212.  
  213.         lib$getdvi (&DVI$_DEVNAM, &ch, 0, 0, &_devname, &len);
  214.         devname[len] = 0;
  215.  
  216.         Update_Lock (lockid, devname);
  217.     }
  218.  
  219. #ifdef DEBUG
  220.     /* Print logical and device names of mailbox.
  221.      */
  222.     {
  223.     char    dbgmsg[80];
  224.  
  225.     strcat (strcat (strcpy (dbgmsg, mbxname), " = "), devname);
  226.     putmsg (dbgmsg, 0);
  227.     }
  228. #endif
  229.  
  230.     /* Get a free channel structure and initialize.
  231.      */
  232.  
  233.     for (i = 0; i < NUM_CHANNELS; i++)
  234.         if (channels[i].mbx_chan == 0)
  235.         break;
  236.  
  237.     if (i == NUM_CHANNELS) {
  238.         putmsg ("ZFIOVI: No channel structures available", 0);
  239.         sys$dassgn (ch);
  240.         Dequeue_Lock (lockid);
  241.         *chan = XERR;
  242.     } else {
  243.         CHANNEL    *c;
  244.  
  245.         c = &channels[i];
  246.  
  247.         c->mbx_chan   = ch;
  248.         c->lockid     = lockid;
  249.         c->qio_status = 0;
  250.         c->ast        = 0;
  251.         c->ast_arg    = 0;
  252.  
  253.         if (*mode == READ_ONLY) {
  254.         /* Check if there are messages waiting to be read.
  255.          * This number is in the two low-order bytes of the
  256.          * device-dependent longword.
  257.          */
  258.         lib$getdvi (&DVI$_DEVDEPEND, &ch, 0, &c->io_pending);
  259.         c->io_pending &= 0x0000FFFF;
  260.         } else {
  261.         c->io_pending = 0;
  262.         }
  263.  
  264.         *chan = c;
  265.     }
  266. }
  267.  
  268.  
  269. /*
  270.  * ZCLSVI -- Close IRAF-Imtool connection.
  271.  */
  272.  
  273. zclsvi (chan, status)
  274. XINT    *chan;
  275. XINT    *status;
  276. {
  277.     CHANNEL    *c;
  278.     int    stat;
  279.  
  280.     /* Deassign the mailbox channel, dequeue the associated lock,
  281.      * and "free" the channel structure.
  282.      */
  283.  
  284.     c = *chan;
  285.  
  286.     stat = sys$dassgn (c->mbx_chan);
  287.     if VMS_ERROR(stat) {
  288.         putmsg ("ZFIOVI: Error deassigning mailbox channel", stat);
  289.         *status = XERR;
  290.     } else {
  291.         *status = XOK;
  292.     }
  293.  
  294.     Dequeue_Lock (c->lockid);
  295.  
  296.     c->mbx_chan = 0;
  297. }
  298.  
  299.  
  300. /*
  301.  * ZARDVI -- Asynchronous read on IRAF-Imtool channel.
  302.  */
  303.  
  304. zardvi (chan, buf, maxbytes, loffset)
  305. XINT    *chan;
  306. char    *buf;
  307. XINT    *maxbytes;
  308. XLONG    *loffset;            /* ignored */
  309. {
  310.     CHANNEL    *c;
  311.  
  312.     /* Post a read on the mailbox.
  313.      */
  314.  
  315.     c = *chan;
  316.  
  317.     c->qio_status = sys$qio (0, c->mbx_chan, IO$_READVBLK,
  318.                 &c->iosb, 0, 0,
  319.                 buf, *maxbytes, 0,0,0,0);
  320. }
  321.  
  322.  
  323. /*
  324.  * ZAWRVI -- Asynchronous write to IRAF-Imtool channel.
  325.  */
  326.  
  327. zawrvi (chan, buf, nbytes, loffset)
  328. XINT    *chan;
  329. char    *buf;
  330. XINT    *nbytes;
  331. XLONG    *loffset;            /* ignored */
  332. {
  333.     CHANNEL    *c;
  334.  
  335.     /* Write to the mailbox.
  336.      */
  337.  
  338.     c = *chan;
  339.  
  340.     c->qio_status = sys$qio (0, c->mbx_chan, IO$_WRITEVBLK | IO$M_NOW,
  341.                 &c->iosb, 0, 0,
  342.                 buf, *nbytes, 0,0,0,0);
  343. }
  344.  
  345.  
  346. /*
  347.  * ZAWTVI -- Wait for asynchronous read/write to complete.
  348.  */
  349.  
  350. zawtvi (chan, status)
  351. XINT    *chan;
  352. XINT    *status;
  353. {
  354.     CHANNEL    *c;
  355.  
  356.     /* Wait on i/o to complete.
  357.      */
  358.  
  359.     c = *chan;
  360.  
  361.     sys$synch (0, &c->iosb);
  362.  
  363.     if VMS_ERROR(c->qio_status) {
  364.         putmsg ("ZFIOVI: QIO error", c->qio_status);
  365.         *status = XERR;
  366.  
  367.     } else if (c->iosb.status == SS$_ENDOFFILE) {
  368.         putmsg ("ZFIOVI: Unexpected EOF on mailbox", c->iosb.status);
  369.         *status = 0;
  370.  
  371.     } else if VMS_ERROR(c->iosb.status) {
  372.         putmsg ("ZFIOVI: Mailbox i/o error", c->iosb.status);
  373.         *status = XERR;
  374.  
  375.     } else {
  376.         *status = c->iosb.count;
  377.  
  378.         if (c->io_pending)
  379.         c->io_pending--;
  380.     }
  381. }
  382.  
  383.  
  384.  
  385. /*
  386.  * ZBufSize -- Return maximum i/o transfer size, in bytes.
  387.  */
  388.  
  389. ZBufSize (CHANNEL *c)
  390. {
  391.     return SZ_MBX;
  392. }
  393.  
  394.  
  395. /*
  396.  * ZPending -- Return data pending count.
  397.  */
  398.  
  399. ZPending (CHANNEL *c)
  400. {
  401.     return c->io_pending;
  402. }
  403.  
  404.  
  405. /*
  406.  * ZSelectAsyncInput -- Set up for asynchronous notification when i/o is
  407.  *    ready on a given channel.  This must be reset by the application
  408.  *    _after_ it has read the data from the mailbox.
  409.  */
  410.  
  411. ZSelectAsyncInput (CHANNEL *c, int (*ast)(), int ast_arg)
  412. {
  413.     if (ast)
  414.         Set_Attn_AST (c);
  415.  
  416.     c->ast     = ast;
  417.     c->ast_arg = ast_arg;
  418. }
  419.  
  420.  
  421. /*
  422.  * Set_Attn_AST -- Set a write attention AST on the mailbox channel
  423.  */
  424.  
  425. static
  426. Set_Attn_AST (CHANNEL *c)
  427. {
  428.     int    Attn_AST();
  429.     int    stat;
  430.  
  431.  
  432.     stat = sys$qiow (0, c->mbx_chan, IO$_SETMODE | IO$M_WRTATTN,
  433.                 &c->iosb, 0, 0,
  434.                 Attn_AST, c, 0,0,0,0);
  435.     if VMS_ERROR(stat)
  436.         putmsg ("ZFIOVI: Error setting write attn ast", stat);
  437.     else if VMS_ERROR(c->iosb.status)
  438.         putmsg ("ZFIOVI: Error setting write attn ast", c->iosb.status);
  439. }
  440.  
  441.  
  442. /*
  443.  * Attn_AST -- Write attention AST routine.  Increment the i/o pending
  444.  *    count and invoke the application's AST routine.
  445.  */
  446.  
  447. static
  448. Attn_AST (CHANNEL *c)
  449. {
  450.     c->io_pending++;
  451.  
  452.     if (c->ast)
  453.         (* c->ast) (c->ast_arg);
  454. }
  455.  
  456.  
  457. /*
  458.  * PUTMSG - Put an error message and/or an associated system error
  459.  *    string to SYS$OUTPUT.
  460.  */
  461.  
  462. static
  463. putmsg (char *msg, int code)
  464. {
  465.     DESC    _msg;
  466.  
  467.     struct {
  468.         short    arg_count;
  469.         short    msg_options;
  470.         int        msg_code;
  471.     } msgvec;
  472.  
  473.     if (msg) {
  474.         SET_DESCRIPTOR (_msg, msg, strlen(msg));
  475.  
  476.         lib$put_output (&_msg);
  477.     }
  478.  
  479.     if (code) {
  480.         msgvec.arg_count   = 1;
  481.         msgvec.msg_options = 0x0F;        /* output full message */
  482.         msgvec.msg_code    = code;
  483.  
  484.         sys$putmsg (&msgvec);
  485.     }
  486. }
  487.  
  488.  
  489. /*
  490.  * ENQUEUE_LOCK -- Enqueue an exclusive lock.  Copies the value from the
  491.  *    lock status block.  The function return value is the lock id for
  492.  *    subsequent lock operations, or zero if there was an error.
  493.  *
  494.  *    Locks are cluster-wide by default, so the local node name is added
  495.  *    to the specified lock name.  This will prevent conflicts in cases
  496.  *    where shared accounts are used in a VAX cluster environment.
  497.  */
  498.  
  499. static
  500. int Enqueue_Lock (char *lockname, char *valblk)
  501. {
  502.     int    stat, sys$enqw();
  503.     short    len;
  504.  
  505.     LOCK    lblock;
  506.  
  507.     char    nodename[SZ_NODENAME+1];
  508.     DESC    _nodename;
  509.  
  510.     char    resnam[SZ_LOCKNAME*2];
  511.     DESC    _resnam;
  512.  
  513.     /* Generate the lock resource name of the form: lockname@nodename.
  514.      * This string is truncated if necessary.
  515.      */
  516.  
  517.     SET_DESCRIPTOR (_nodename, nodename, sizeof(nodename)-1);
  518.  
  519.     lib$getsyi (&SYI$_NODENAME, 0, &_nodename, &len, 0, 0);
  520.     nodename[len] = 0;
  521.  
  522.     strcat (strcat (strcpy (resnam, lockname), "@"), nodename);
  523.     resnam[SZ_LOCKNAME] = 0;
  524.  
  525.     SET_DESCRIPTOR (_resnam, resnam, strlen(resnam));
  526.  
  527.     /* Enqueue the lock.
  528.      */
  529.  
  530.     stat = sys$enqw (
  531.             0,            /* event flag        */
  532.             LCK$K_EXMODE,        /* lock mode        */
  533.             &lblock,        /* lock status block     */
  534.             LCK$M_VALBLK,        /* flags (get value block) */
  535.             &_resnam,        /* resource name     */
  536.             0,            /* parent id        */
  537.             0,            /* AST address        */
  538.             0,            /* AST parameter    */
  539.             0,            /* Blocking AST        */
  540.             0,            /* access mode        */
  541.             0            /* null argument    */
  542.             );
  543.  
  544.     if VMS_ERROR(stat) {
  545.         putmsg ("ZFIOVI: Error enqueueing lock request", stat);
  546.         return 0;
  547.     }
  548.     if VMS_ERROR(lblock.status) {
  549.         putmsg ("ZFIOVI: Lock error from enqueue request", lblock.status);
  550.         return 0;
  551.     }
  552.  
  553.     if (valblk)
  554.         lib$movc3 (&SZ_LOCKVALBLK, lblock.valblk, valblk);
  555.  
  556.     return lblock.lkid;
  557. }
  558.  
  559.  
  560. /*
  561.  * UPDATE_LOCK -- Update a lock value block by converting it to null mode.
  562.  *    This keeps the lock and its value block around for other processes
  563.  *    to access later.
  564.  */
  565.  
  566. static
  567. Update_Lock (int lockid, char *valblk)
  568. {
  569.     int    stat, sys$enqw();
  570.     int    flags;
  571.     LOCK    lblock;
  572.  
  573.     lblock.lkid = lockid;
  574.  
  575.     flags = LCK$M_CONVERT;
  576.  
  577.     if (valblk) {
  578.         flags |= LCK$M_VALBLK;
  579.         lib$movc3 (&SZ_LOCKVALBLK, valblk, lblock.valblk);
  580.     }
  581.  
  582.     /* Enqueue the lock (conversion).
  583.      */
  584.  
  585.     stat = sys$enqw (
  586.             0,            /* event flag        */
  587.             LCK$K_NLMODE,        /* lock mode        */
  588.             &lblock,        /* lock status block     */
  589.             flags,            /* flags        */
  590.             0,            /* resource name     */
  591.             0,            /* parent id        */
  592.             0,            /* AST address        */
  593.             0,            /* AST parameter    */
  594.             0,            /* Blocking AST        */
  595.             0,            /* access mode        */
  596.             0            /* null argument    */
  597.             );
  598.  
  599.     if VMS_ERROR(stat)
  600.         putmsg ("ZFIOVI: Error enqueueing lock conversion request", stat);
  601.  
  602.     if VMS_ERROR(lblock.status)
  603.         putmsg ("ZFIOVI: Lock error from conversion request", lblock.status);
  604. }
  605.  
  606.  
  607. /*
  608.  * DEQUEUE_LOCK - Dequeue a lock.
  609.  */
  610.  
  611. static
  612. Dequeue_Lock (int lockid)
  613. {
  614.     int    stat, sys$deq();
  615.  
  616.     stat = sys$deq (
  617.             lockid,            /* lock identification    */
  618.             0,            /* value block        */
  619.             0,            /* access mode        */
  620.             0            /* $DEQ options        */
  621.             );
  622.  
  623.     if VMS_ERROR(stat)
  624.         putmsg ("ZFIOVI: Error dequeueing lock", stat);
  625. }
  626.  
  627.  
  628.  
  629.  
  630. #ifdef REQUIRES_GRPNAM_PRIVILEGE
  631.  
  632.     /**
  633.      ** This is here for reference only.  It's use has been superseded
  634.      ** by the use of locks above.  If GRPNAM privilege is assumed,
  635.      ** this function is called before a call to SYS$CREMBX, whether
  636.      ** or not the mailbox has already been created.
  637.      **/
  638.  
  639. #include <lnmdef.h>    /* Logical name definitions        */
  640. #include <prvdef.h>    /* Privilege mask definitions        */
  641.  
  642. /*
  643.  * SET_GROUP_TABLE -- Set logical so that mailbox logical name is put
  644.  * into the group table.  Note that we cannot use LIB$SET_LOGICAL here
  645.  * (the easier approach) since this software will be run as part of a
  646.  * VMS/IRAF subprocess which has no CLI associated with it.  If the
  647.  * process does not currently have GRPNAM privilege, issue a warning.
  648.  * This will be more useful than getting a mailbox creation error.
  649.  */
  650.  
  651. static
  652. set_group_table ( )
  653. {
  654.     $DESCRIPTOR (_name,  "LNM$TEMPORARY_MAILBOX");
  655.     $DESCRIPTOR (_value, "LNM$GROUP");
  656.     $DESCRIPTOR (_table, "LNM$PROCESS_DIRECTORY");
  657.  
  658.     struct item_list {
  659.         short    len;        /* length of buffer */
  660.         short    code;        /* service-dependent option code */
  661.         char    *buf;        /* user buffer */
  662.         short    *blen;        /* return length for data in buf */
  663.     } itmlst[2];
  664.  
  665.     int    stat, curpriv;
  666.     char    fallback_msg[] = 
  667.         "Cannot set GROUP table for mailbox name; using JOB table";
  668.  
  669.     lib$getjpi (&JPI$_CURPRIV, 0, 0, &curpriv, 0, 0);
  670.  
  671.     if (curpriv & PRV$M_GRPNAM) {
  672.  
  673.         itmlst[0].len  = _value.dsc$w_length;
  674.         itmlst[0].code = LNM$_STRING;
  675.         itmlst[0].buf  = _value.dsc$a_pointer;
  676.         itmlst[0].blen = 0;
  677.         itmlst[1].len  = 0;
  678.         itmlst[1].code = 0;
  679.  
  680.         stat = sys$crelnm (&LNM$M_NO_ALIAS, &_table, &_name, 0, itmlst);
  681.         if VMS_ERROR(stat)
  682.         putmsg (fallback_msg, stat);
  683.     } else {
  684.         putmsg (fallback_msg, 0);
  685.     }
  686. }
  687.  
  688. #endif
  689.  
  690.