Back to index

lightning-sunbird  0.9+nobinonly
ltermManager.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is lineterm.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Ramalingam Saravanan.
00018  * Portions created by the Initial Developer are Copyright (C) 1999
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /* ltermManager.c: LTERM control operations
00038  */
00039 
00040 /* public declarations */
00041 #include "lineterm.h"
00042 
00043 /* private declarations */
00044 #include "ltermPrivate.h"
00045 
00046 /* LTERM global variables */
00047 LtermGlobal ltermGlobal;
00048 
00049 #define CHECK_IF_VALID_LTERM_NUMBER(procname,lterm)  \
00050   if ((lterm < 0) || (lterm >= MAXTERM)) {           \
00051     LTERM_ERROR("procname: Error - LTERM index %d out of range\n", \
00052                 lterm);                              \
00053     return -1;                                       \
00054   }
00055 
00056 /* Macros for use in thread synchronization */
00057 #define GLOBAL_LOCK   MUTEX_LOCK(ltermGlobal.listMutex)
00058 #define GLOBAL_UNLOCK MUTEX_UNLOCK(ltermGlobal.listMutex)
00059 
00060 /* The following macros assume that ltermGlobal.listMutex has already been
00061  * locked.
00062  * A UNILOCK is lock that can be locked only by one thread at a time;
00063  * attempts to lock it by another thread result in an error return.
00064  * This can be used to detect violations of thread discipline.
00065  */
00066 
00067 #define SWITCH_GLOBAL_TO_UNILOCK(procname,mutex,mutex_flag)  \
00068   if (lts->mutex_flag) {                 \
00069     LTERM_ERROR("procname: Error - MUTEX mutex already locked\n", \
00070                 lts->mutex);   \
00071     MUTEX_UNLOCK(ltermGlobal.listMutex); \
00072     return -1;                           \
00073   }                                      \
00074   MUTEX_LOCK(lts->mutex);                \
00075   lts->mutex_flag = 1;                   \
00076   MUTEX_UNLOCK(ltermGlobal.listMutex);
00077 
00078 #define RELEASE_UNILOCK(mutex,mutex_flag)  \
00079   lts->mutex_flag = 0;      \
00080   MUTEX_UNLOCK(lts->mutex);
00081 
00082 
00083 int ltermClose(struct lterms *lts);
00084 static int ltermShellInit(struct lterms *lts, int all);
00085 static int ltermCreatePipe(FILEDESC *writePIPE, FILEDESC *readPIPE);
00086 static int ltermCreateProcess(struct LtermProcess *ltp,
00087                               char *const argv[], int nostderr, int noexport);
00088 static void ltermDestroyProcess(struct LtermProcess *ltp);
00089 
00090 
00095 int lterm_init(int messageLevel)
00096 {
00097   if (!ltermGlobal.initialized) {
00098     int result, j;
00099     /* Assert that INTs have at least 32-bit precision.
00100      * This assumption underlies the choice of various bit codes.
00101      */
00102     assert(sizeof(int) >= 4);
00103 
00104     if (sizeof(UNICHAR) == 2) {
00105       /* Assert that the maximum column count fits into an UNICHAR.
00106        * This assumption is used for input buffer pipe I/O by lterm_write
00107        * and ltermWrite, but could be relaxed by using different buffer record
00108        * structure.
00109        */
00110       assert(MAXCOL <= 0x7FFF);
00111     }
00112 
00113     /* Initialize lock for accessing LTERMs array */
00114     if (MUTEX_INITIALIZE(ltermGlobal.listMutex) != 0)
00115       return -1;
00116 
00117     /* Initialize trace/log module for STDERR;
00118      * this could perhaps be eliminated, if TLOG_PRINT is redefined to PR_LOG
00119      * and so on, with the USE_NSPR_IO option enabled.
00120      */
00121     tlog_init(stderr);
00122     result = tlog_set_level(LTERM_TLOG_MODULE, messageLevel, NULL);
00123 
00124     /* Meta input delimiter */
00125     ltermGlobal.metaDelimiter = U_COLON;
00126 
00127     /* String of five ASCII characters escaped in XML;
00128      * only the first four characters are escaped in HTML;
00129      * it is sufficient to escape just the first three characters, "&<>"
00130      * when inserting plain text (outside markup) in XML/HTML fragments
00131      */
00132     ltermGlobal.escapeChars[LTERM_AMP_ESCAPE]  = '&';
00133     ltermGlobal.escapeChars[LTERM_LT_ESCAPE]   = '<';
00134     ltermGlobal.escapeChars[LTERM_GT_ESCAPE]   = '>';
00135     ltermGlobal.escapeChars[LTERM_QUOT_ESCAPE] = '\"';
00136     ltermGlobal.escapeChars[LTERM_APOS_ESCAPE] = '\'';
00137     ltermGlobal.escapeChars[LTERM_XML_ESCAPES] = 0;
00138 
00139     /* List of XML character escape sequences (Unicode) */
00140     ucscopy(ltermGlobal.escapeSeq[LTERM_AMP_ESCAPE],  "&amp;",
00141               LTERM_MAXCHAR_ESCAPE+1);
00142     ucscopy(ltermGlobal.escapeSeq[LTERM_LT_ESCAPE],   "&lt;",
00143               LTERM_MAXCHAR_ESCAPE+1);
00144     ucscopy(ltermGlobal.escapeSeq[LTERM_GT_ESCAPE],   "&gt;",
00145               LTERM_MAXCHAR_ESCAPE+1);
00146     ucscopy(ltermGlobal.escapeSeq[LTERM_QUOT_ESCAPE], "&quot;",
00147               LTERM_MAXCHAR_ESCAPE+1);
00148     ucscopy(ltermGlobal.escapeSeq[LTERM_APOS_ESCAPE], "&apos;",
00149               LTERM_MAXCHAR_ESCAPE+1);
00150 
00151     /* Escape sequence lengths (including delimiters) */
00152     for (j=0; j<LTERM_XML_ESCAPES; j++) {
00153       ltermGlobal.escapeLen[j] = ucslen(ltermGlobal.escapeSeq[j]);
00154       assert(ltermGlobal.escapeLen[j] <= LTERM_MAXCHAR_ESCAPE);
00155     }
00156 
00157     /* LTERM global initialization flag */
00158     ltermGlobal.initialized = 1;
00159 
00160   } else {
00161     LTERM_WARNING("lterm_init: Warning - already initialized\n");
00162   }
00163 
00164   return 0;
00165 }
00166 
00167 
00174 int lterm_new()
00175 {
00176   int lterm;
00177   struct lterms *lts;
00178 
00179   if (!ltermGlobal.initialized) {
00180     LTERM_ERROR("lterm_new: Error - call lterm_init() to initialize LTERM library\n");
00181     return -1;
00182   }
00183 
00184   LTERM_LOG(lterm_new,10,("Creating LTERM ...\n"));
00185 
00186   /* Allocate memory for new LTERMS structure: needs clean-up */
00187   lts = (struct lterms *) MALLOC(sizeof(struct lterms));
00188 
00189   if (lts == NULL) {
00190     LTERM_ERROR("lterm_new: Error - failed to allocate memory for LTERM\n");
00191     return -1;
00192   }
00193 
00194   GLOBAL_LOCK;
00195 
00196   for (lterm=0; lterm < MAXTERM; lterm++) {
00197     if (ltermGlobal.termList[lterm] == NULL)
00198       break;
00199   }
00200 
00201   if (lterm == MAXTERM) {
00202     LTERM_ERROR( "lterm_new: Error - too many LTERMS; recompile with increased MAXTERM\n");
00203     FREE(lts);  /* Clean up memory allocation for LTERMS structure */
00204     GLOBAL_UNLOCK;
00205     return -1;
00206   }
00207   
00208   /* Insert LTERM pointer in list of LTERMs */
00209   ltermGlobal.termList[lterm] = lts;
00210 
00211   MUTEX_INITIALIZE(lts->adminMutex);
00212 
00213   /* Administrative thread inactive */
00214   lts->adminMutexLocked = 0;
00215 
00216   /* LTERM not yet opened for I/O */
00217   lts->opened = 0;
00218 
00219   GLOBAL_UNLOCK;
00220 
00221   LTERM_LOG(lterm_new,11,("created lterm = %d\n", lterm));
00222 
00223   return lterm;
00224 }
00225 
00226 
00227 /* Macro to close and delete LTERM */
00228 #define LTERM_OPEN_ERROR_RETURN(lterm,lts,errmsg) \
00229   LTERM_ERROR(errmsg);  \
00230   ltermClose(lts);      \
00231   RELEASE_UNILOCK(adminMutex,adminMutexLocked) \
00232   lterm_delete(lterm);  \
00233   return -1;
00234 
00239 int lterm_open(int lterm, char *const argv[],
00240                const char* cookie, const char* init_command,
00241                const UNICHAR* prompt_regexp, int options, int process_type,
00242                int rows, int cols, int x_pixels, int y_pixels,
00243                lterm_callback_func_t callback_func, void *callback_data)
00244 {
00245   int nostderr, noPTY, j;
00246   struct ptys ptyStruc;
00247   struct lterms *lts;
00248   struct LtermInput *lti;
00249   struct LtermOutput *lto;
00250   struct LtermProcess *ltp;
00251   char *const *cargv;
00252   char *defaultArgs[3];
00253 
00254   if (!ltermGlobal.initialized) {
00255     LTERM_ERROR("lterm_open: Error - call lterm_init() to initialize LTERM library\n");
00256     return -1;
00257   }
00258 
00259   CHECK_IF_VALID_LTERM_NUMBER(lterm_open,lterm)
00260 
00261   LTERM_LOG(lterm_open,10,("Opening LTERM %d\n", lterm));
00262 
00263   GLOBAL_LOCK;
00264 
00265   lts = ltermGlobal.termList[lterm];
00266 
00267   if (lts == NULL) {
00268     LTERM_ERROR("lterm_open: Error - LTERM %d not created using lterm_new\n",
00269                 lterm);
00270     GLOBAL_UNLOCK;
00271     return -1;
00272   }
00273 
00274   if (lts->opened) {
00275     LTERM_ERROR("lterm_open: Error - LTERM %d is already open\n",
00276                 lterm);
00277     GLOBAL_UNLOCK;
00278     return -1;
00279   }
00280 
00281   SWITCH_GLOBAL_TO_UNILOCK(lterm_open,adminMutex,adminMutexLocked)
00282 
00283   /* Pointers to LTERM process/input/output structures */
00284   ltp = &(lts->ltermProcess);
00285   lti = &(lts->ltermInput);
00286   lto = &(lts->ltermOutput);
00287 
00288   /* Initialize flags/pointers/file descriptors that need to be cleaned up */
00289   lts->suspended = 0;
00290   lts->ptyMode   = 0;
00291 
00292   /* Initialize TTY control characters */
00293   assert(TTYWERASE < MAXTTYCONTROL);
00294   lts->control[TTYINTERRUPT] = U_CTL_C;
00295   lts->control[TTYERASE]     = U_DEL;
00296   lts->control[TTYKILL]      = U_CTL_U;
00297   lts->control[TTYEOF]       = U_CTL_D;
00298   lts->control[TTYSUSPEND]   = U_CTL_Z;
00299   lts->control[TTYREPRINT]   = U_CTL_R;
00300   lts->control[TTYDISCARD]   = U_CTL_O;
00301   lts->control[TTYWERASE]    = U_CTL_W;
00302 
00303   lts->writeBUFFER =  NULL_FILEDESC;
00304   lts->readBUFFER =   NULL_FILEDESC;
00305 
00306   /* Clear input line break flag */
00307   lts->inputLineBreak = 0;
00308 
00309   /* Clear input buffer */
00310   lti->inputBufBytes = 0;
00311   lts->inputBufRecord = 0;
00312 
00313   /* Clear styleMask */
00314   lto->styleMask = 0;
00315 
00316   /* Clear incomplete raw output buffers */
00317   lto->rawOUTBytes = 0;
00318   lto->rawERRBytes = 0;
00319 
00320   /* Clear decoded output buffer */
00321   lto->decodedChars = 0;
00322   lto->incompleteEscapeSequence = 0;
00323 
00324   /* Set initial screen size */
00325   lts->nRows = rows;
00326   lts->nCols = cols;
00327   lts->xPixels = y_pixels;
00328   lts->yPixels = x_pixels;
00329 
00330   /* Clear screen buffer */
00331   lto->screenChar  = NULL;
00332   lto->screenStyle = NULL;
00333 
00334   lts->outputMutexLocked = 0;
00335 
00336   MUTEX_INITIALIZE(lts->outputMutex);
00337 
00338   /* AT THIS POINT, LTERMCLOSE MAY BE CALLED FOR CLEAN-UP */
00339 
00340   /* Create input buffer pipe; needs clean-up */
00341   if (ltermCreatePipe(&(lts->writeBUFFER), &(lts->readBUFFER)) != 0) {
00342     LTERM_OPEN_ERROR_RETURN(lterm,lts,
00343                 "lterm_open: Error - input buffer pipe creation failed\n")
00344   }
00345 
00346   LTERM_LOG(lterm_open,11,("Created input buffer pipe\n", lterm));
00347 
00348   defaultArgs[0] = NULL;
00349   defaultArgs[1] = "-i";
00350   defaultArgs[2] = NULL;
00351 
00352   if (argv != NULL) {
00353     /* Use supplied command arguments */
00354     cargv= argv;
00355 
00356   } else {
00357     /* Default command arguments */
00358     defaultArgs[0] = (char *) getenv("SHELL");
00359     if (defaultArgs[0] == NULL)
00360       defaultArgs[0] = "/bin/sh";
00361     cargv= defaultArgs;
00362   }
00363 
00364   if (process_type >= 0) {
00365     /* Specified process type code */
00366     lts->processType = process_type;
00367 
00368   } else {
00369     /* Determine process type code from path name */
00370     char *lastSlash;
00371     char *tailStr;
00372 
00373     /* Find the last occurrence of slash */
00374     lastSlash = (char *) strrchr(cargv[0], '/');
00375     if (lastSlash == NULL) {
00376       tailStr = cargv[0];
00377     } else {
00378       tailStr = lastSlash + 1;
00379     }
00380 
00381     if (strcmp(tailStr,"sh") == 0) {
00382       lts->processType = LTERM_SH_PROCESS;
00383 
00384     } else if (strcmp(tailStr,"ksh") == 0) {
00385       lts->processType = LTERM_KSH_PROCESS;
00386 
00387     } else if (strcmp(tailStr,"bash") == 0) {
00388       lts->processType = LTERM_BASH_PROCESS;
00389 
00390     } else if (strcmp(tailStr,"csh") == 0) {
00391       lts->processType = LTERM_CSH_PROCESS;
00392 
00393     } else if (strcmp(tailStr,"tcsh") == 0) {
00394       lts->processType = LTERM_TCSH_PROCESS;
00395 
00396     } else {
00397       lts->processType = LTERM_UNKNOWN_PROCESS;
00398     }
00399   }
00400 
00401   LTERM_LOG(lterm_open,11,("process type code = %d\n", lts->processType));
00402 
00403   /* TTY options */
00404   lts->options = options;
00405   lts->noTTYEcho = (options & LTERM_NOECHO_FLAG) != 0;
00406   lts->disabledInputEcho = 1;
00407   lts->restoreInputEcho = 1;
00408 
00409   nostderr = (options & LTERM_STDERR_FLAG) == 0;
00410 
00411 #if defined(NO_PTY) || defined(USE_NSPR_IO)
00412   /* Current implementation of pseudo-TTY is incompatible with the NSPR I/O
00413      option; set the USE_NSPR_IO option only on platforms where
00414      PTYSTREAM is not implemented.
00415      Reason for incompatibility: cannot combine the UNIX-style integral
00416      PTY file descriptor and NSPR's PRFileDesc in the same poll function
00417      call.
00418   */
00419   noPTY = 1;
00420 #else
00421   noPTY = (options & LTERM_NOPTY_FLAG);
00422 #endif
00423 
00424   if (noPTY) {
00425     /* Create pipe process: needs clean-up */
00426     if (ltermCreateProcess(ltp, cargv, nostderr, 0) == -1) {
00427 
00428       LTERM_OPEN_ERROR_RETURN(lterm,lts,
00429             "lterm_open: Pipe process creation failed\n")
00430     }
00431 
00432   } else {
00433     /* Create pseudo-TTY with blocking I/O: needs clean-up */
00434     int noblock = 1;
00435     int noexport = 0;
00436     int debugPTY = (tlogGlobal.messageLevel[LTERM_TLOG_MODULE] > 1);
00437     int errfd = nostderr?-1:-2;
00438 
00439     LTERM_LOG(lterm_open,11,
00440               ("errfd=%d, noecho=%d, noblock=%d, noexport=%d, debugPTY=%d\n",
00441                errfd, lts->noTTYEcho, noblock, noexport, debugPTY));
00442 #ifndef NO_PTY
00443     if (pty_create(&ptyStruc, cargv,
00444                    lts->nRows, lts->nCols, lts->xPixels, lts->yPixels,
00445                    errfd, noblock, lts->noTTYEcho, noexport, debugPTY) == -1) {
00446       LTERM_OPEN_ERROR_RETURN(lterm,lts,
00447             "lterm_open: Error - PTY creation failed\n")
00448     }
00449 #endif  /* !NO_PTY */
00450 
00451     /* Copy PTY structure */
00452     lts->pty = ptyStruc;
00453     lts->ptyMode = 1;
00454   }
00455 
00456   /* Set up LtermOutput polling structure */
00457   lto->pollFD[POLL_INPUTBUF].fd = lts->readBUFFER;
00458   lto->pollFD[POLL_INPUTBUF].POLL_EVENTS = POLL_READ;
00459 
00460   if (lts->ptyMode) {
00461 #ifndef USE_NSPR_IO
00462     lto->pollFD[POLL_STDOUT].fd = lts->pty.ptyFD;
00463     lto->pollFD[POLL_STDOUT].POLL_EVENTS = POLL_READ;
00464 
00465     if (VALID_FILEDESC(lts->pty.errpipeFD)) {
00466       lto->pollFD[POLL_STDERR].fd = lts->pty.errpipeFD;
00467       lto->pollFD[POLL_STDERR].POLL_EVENTS = POLL_READ;
00468       lto->nfds = POLL_COUNT;
00469     } else {
00470         lto->nfds = POLL_COUNT-1;
00471     }
00472 #else
00473     /* Current implementation of pseudo-TTY incompatible with USE_NSPR_IO */
00474     LTERM_OPEN_ERROR_RETURN(lterm,lts,
00475            "lterm_open: Error - pseudo-TTY incompatible with USE_NSPR_IO\n")
00476 #endif
00477 
00478   } else {
00479     lto->pollFD[POLL_STDOUT].fd = ltp->processOUT;
00480     lto->pollFD[POLL_STDOUT].POLL_EVENTS = POLL_READ;
00481 
00482     if (VALID_FILEDESC(ltp->processERR)) {
00483       lto->pollFD[POLL_STDERR].fd = ltp->processERR;
00484       lto->pollFD[POLL_STDERR].POLL_EVENTS = POLL_READ;
00485       lto->nfds = POLL_COUNT;
00486     } else {
00487       lto->nfds = POLL_COUNT-1;
00488     }
00489   }
00490 
00491   /* No callbacks initially associated with this LTERM */
00492   for (j=0; j<lto->nfds; j++)
00493     lto->callbackTag[j] = 0;
00494 
00495   /* Determine whether STDERR should be read before STDOUT */
00496   if (lts->processType == LTERM_TCSH_PROCESS) {
00497     lts->readERRfirst = 0;
00498   } else {
00499     lts->readERRfirst = 1;
00500   }
00501 
00502   /* Determine whether STDERR and STDOUT should be interleaved */
00503   lts->interleave = 1;
00504 
00505   /* Determine maximum value for input mode */
00506   if (options & LTERM_NOCANONICAL_FLAG)
00507     lts->maxInputMode = LTERM0_RAW_MODE;
00508 
00509   else if (options & LTERM_NOEDIT_FLAG)
00510     lts->maxInputMode = LTERM1_CANONICAL_MODE;
00511 
00512   else if ((options & LTERM_NOCOMPLETION_FLAG) ||
00513            lts->noTTYEcho)
00514     lts->maxInputMode = LTERM2_EDIT_MODE;
00515 
00516   else {
00517     if ((lts->processType == LTERM_BASH_PROCESS) ||
00518         (lts->processType == LTERM_TCSH_PROCESS))
00519       lts->maxInputMode = LTERM3_COMPLETION_MODE;
00520     else
00521       lts->maxInputMode = LTERM2_EDIT_MODE;
00522   }
00523 
00524   LTERM_LOG(lterm_open,11,("options=0x%x, processType=%d, readERRfirst=%d, maxInputMode=%d\n",
00525                 lts->options, lts->processType, lts->readERRfirst, lts->maxInputMode));
00526   LTERM_LOGUNICODE(lterm_open,11,(prompt_regexp, (int) ucslen(prompt_regexp) ));
00527 
00528   if (cookie != NULL) {
00529     /* Copy cookie string (no more than MAXCOOKIESTR-1 characters) */
00530     if (strlen(cookie) > MAXCOOKIESTR-1) {
00531       LTERM_WARNING("lterm_open: Warning - cookie string truncated\n");
00532     }
00533     strncpy(lts->cookie, cookie, MAXCOOKIESTR-1);
00534     lts->cookie[MAXCOOKIESTR-1] = '\0';
00535   } else {
00536     /* No cookie */
00537     lts->cookie[0] = '\0';
00538   }
00539 
00540   LTERM_LOG(lterm_open,11,("cookie='%s'\n",lts->cookie));
00541 
00542   /* Shell initialization commands have not been sent yet */
00543   lts->shellInitCommands = 0;
00544 
00545   /* NOTE: Shell init commands are stored in reverse order of execution */
00546 
00547   if (init_command != NULL) {
00548     /* User supplied shell init command */
00549 
00550     if (strlen(init_command) <= MAXSHELLINITSTR-1) {
00551       int cmd = lts->shellInitCommands++;
00552       assert(cmd < MAXSHELLINITCMD);
00553 
00554       (void) strncpy(lts->shellInitStr[cmd], init_command, MAXSHELLINITSTR-1);
00555       lts->shellInitStr[cmd][MAXSHELLINITSTR-1] = '\0';
00556 
00557     } else {
00558       LTERM_WARNING("lterm_open: Warning - user init command too long\n");
00559     }
00560   }
00561 
00562   if (lts->processType != LTERM_UNKNOWN_PROCESS) {
00563     /* Process dependent shell init command */
00564     int result;
00565     char* shellInitFormat;
00566 
00567     if ((lts->processType == LTERM_CSH_PROCESS) ||
00568       (lts->processType == LTERM_TCSH_PROCESS)) {
00569       /* C-shell family */
00570       shellInitFormat = "setenv LTERM_COOKIE '%s'\n";
00571 
00572     } else {
00573       /* Bourne-shell family */
00574       shellInitFormat = "LTERM_COOKIE='%s'; export LTERM_COOKIE\n";
00575     }
00576 
00577     /* **** WATCH OUT FOR BUFFER OVERFLOW!!! *** */
00578     if (strlen(shellInitFormat)-4+strlen(lts->cookie) <= MAXSHELLINITSTR-1) {
00579 
00580       int cmd = lts->shellInitCommands++;
00581       assert(cmd < MAXSHELLINITCMD);
00582 
00583       sprintf(lts->shellInitStr[cmd], shellInitFormat, lts->cookie);
00584       lts->shellInitStr[cmd][MAXSHELLINITSTR-1] = '\0';
00585 
00586     } else {
00587       LTERM_WARNING("lterm_open: Warning - process init command too long\n");
00588     }
00589   }
00590 
00591   for (j=lts->shellInitCommands-1; j>0; j--) {
00592     LTERM_LOG(lterm_open,11,("shellInitStr%d='%s'\n",j,lts->shellInitStr[j]));
00593   }
00594 
00595   /* Initialize regular expression string matching command prompt */
00596   if ((ucslen(prompt_regexp) == 0) ||
00597       (ucslen(prompt_regexp) > MAXPROMPT-1)) {
00598     LTERM_OPEN_ERROR_RETURN(lterm,lts,
00599          "lterm_open: Error - prompt_regexp string too short/long\n")
00600   }
00601 
00602   ucsncpy( lts->promptRegexp, prompt_regexp, MAXPROMPT);
00603 
00604   /* Command number counter */
00605   lts->lastCommandNum = 0;
00606 
00607   /* Command completion request code */
00608   lts->completionRequest = LTERM_NO_COMPLETION;
00609 
00610   /* Clear output line buffer and switch to line output mode */
00611   ltermClearOutputLine(lts);
00612   lto->outputMode = LTERM2_LINE_MODE;
00613   lto->styleMask = 0;
00614 
00615   /* Reset terminal modes */
00616   lto->insertMode = 0;
00617   lto->automaticNewline = 0;
00618 
00619   /* Clear input line buffer */
00620   ltermClearInputLine(lts);
00621 
00622   /* Clear input line flag not yet set */
00623   lti->clearInputLine = 0;
00624 
00625   /* Reset input opcodes */
00626   lti->inputOpcodes = 0;
00627 
00628   /* Open LTERM */
00629   lts->opened = 1;
00630 
00631   /* Initialize any callbacks associated with this LTERM */
00632   if (callback_func != NULL) {
00633 #if !defined(USE_NSPR_IO) && defined(USE_GTK_WIDGETS)
00634     LTERM_LOG(lterm_open,11,("Initializing %d callbacks for LTERM %d\n",
00635                                lto->nfds, lterm));
00636 
00637     for (j=0; j<lto->nfds; j++) {
00638       lto->callbackTag[j] = gdk_input_add(lto->pollFD[j].fd,
00639                                           GDK_INPUT_READ,
00640                                           callback_func,
00641                                           (gpointer) callback_data);
00642       if (lto->callbackTag[j] == 0) {
00643         LTERM_OPEN_ERROR_RETURN(lterm,lts,
00644                     "lterm_open: Error - unable to add callback\n")
00645       }
00646     }
00647 #else
00648     LTERM_OPEN_ERROR_RETURN(lterm,lts,
00649       "lterm_open: Error - callbacks not implemented for this configuration\n")
00650 #endif
00651   }
00652 
00653 
00654   RELEASE_UNILOCK(adminMutex,adminMutexLocked)
00655 
00656   LTERM_LOG(lterm_open,11,
00657       ("outputMode=%d, inputMode=%d, maxInputMode=%d\n",
00658       lto->outputMode, lti->inputMode,lts->maxInputMode ));
00659 
00660   return 0;
00661 }
00662 
00663 
00670 int ltermClose(struct lterms *lts)
00671 {
00672   struct LtermInput *lti;
00673   struct LtermOutput *lto;
00674   UNICHAR closeMessage[2] = {0, LTERM_WRITE_CLOSE_MESSAGE};
00675   int j;
00676 
00677   LTERM_LOG(ltermClose,10,("Closing LTERM\n"));
00678 
00679   assert(lts != NULL);
00680 
00681   /* Admin thread must be active */
00682   assert(lts->adminMutexLocked);
00683 
00684   /* Suspend LTERM to prevent any further I/O operations */
00685   lts->suspended = 1;
00686 
00687   /* Pointers to LTERM input/output structures */
00688   lti = &(lts->ltermInput);
00689   lto = &(lts->ltermOutput);
00690 
00691   /* Send close signal to any terminate blocked read operation */
00692   WRITE(lts->writeBUFFER, closeMessage, (SIZE_T)(2*sizeof(UNICHAR)) );
00693 
00694   /* Wait for read operations to complete */
00695   MUTEX_LOCK(lts->outputMutex);
00696   MUTEX_UNLOCK(lts->outputMutex);
00697 
00698   /* Destroy output mutex */
00699   MUTEX_DESTROY(lts->outputMutex);
00700 
00701   /* Close input buffer pipe */
00702   if (VALID_FILEDESC(lts->writeBUFFER))
00703     CLOSE(lts->writeBUFFER);
00704 
00705   if (VALID_FILEDESC(lts->readBUFFER))
00706     CLOSE(lts->readBUFFER);
00707 
00708 #ifdef USE_GTK_WIDGETS
00709   /* Remove any callbacks associated with this LTERM */
00710   for (j=0; j<lto->nfds; j++) {
00711     if (lto->callbackTag[j] != 0) {
00712       gdk_input_remove((gint) lto->callbackTag[j]);
00713       lto->callbackTag[j] = 0;
00714     }
00715   }
00716 #endif
00717 
00718   if (lts->ptyMode) {
00719     /* Close PTY */
00720 #ifndef NO_PTY
00721     pty_close(&(lts->pty));
00722 #endif  /* !NO_PTY */
00723 
00724   } else {
00725     /* Destroy process */
00726     ltermDestroyProcess(&(lts->ltermProcess));
00727   }
00728 
00729   /* Free full screen buffers */
00730   if (lto->screenChar != NULL)
00731     FREE(lto->screenChar);
00732 
00733   if (lto->screenStyle != NULL)
00734     FREE(lto->screenStyle);
00735 
00736   /* Close LTERM */
00737   lts->opened = 0;
00738 
00739   LTERM_LOG(ltermClose,11,("LTERM closed\n"));
00740 
00741   return 0;
00742 }
00743 
00744 
00749 int lterm_close(int lterm)
00750 {
00751   struct lterms *lts;
00752   int returnCode;
00753 
00754   CHECK_IF_VALID_LTERM_NUMBER(lterm_close,lterm)
00755 
00756   LTERM_LOG(lterm_close,10,("Closing LTERM %d\n", lterm));
00757 
00758   GLOBAL_LOCK;
00759 
00760   lts = ltermGlobal.termList[lterm];
00761 
00762   if (lts == NULL) {
00763     /* Forgiving return for non-existent LTERM */
00764     GLOBAL_UNLOCK;
00765     return 0;
00766   }
00767 
00768   if (!lts->opened) {
00769     LTERM_WARNING("lterm_close: Error - LTERM %d not opened\n", lterm);
00770     GLOBAL_UNLOCK;
00771     return -1;
00772   }
00773 
00774   SWITCH_GLOBAL_TO_UNILOCK(lterm_close,adminMutex,adminMutexLocked)
00775 
00776   returnCode = ltermClose(lts);
00777 
00778   RELEASE_UNILOCK(adminMutex,adminMutexLocked)
00779 
00780   return returnCode;
00781 }
00782 
00783 
00788 int lterm_delete(int lterm)
00789 {
00790   struct lterms *lts;
00791   int returnCode;
00792 
00793   CHECK_IF_VALID_LTERM_NUMBER(lterm_delete,lterm)
00794 
00795   LTERM_LOG(lterm_delete,10,("Closing LTERM %d\n", lterm));
00796 
00797   GLOBAL_LOCK;
00798 
00799   lts = ltermGlobal.termList[lterm];
00800 
00801   if (lts == NULL) {
00802     /* Forgiving return for non-existent LTERM */
00803     GLOBAL_UNLOCK;
00804     return 0;
00805   }
00806 
00807   /* Remove LTERM structure from list */
00808   ltermGlobal.termList[lterm] = NULL;
00809 
00810   SWITCH_GLOBAL_TO_UNILOCK(lterm_open,adminMutex,adminMutexLocked)
00811 
00812   returnCode = 0;
00813   if (lts->opened) {
00814     /* Opened LTERM; close it */
00815     returnCode = ltermClose(lts);
00816   }
00817 
00818   RELEASE_UNILOCK(adminMutex,adminMutexLocked)
00819 
00820   MUTEX_DESTROY(lts->adminMutex);
00821 
00822   /* Free memory for LTERMS structure */
00823   FREE(lts);
00824 
00825   LTERM_LOG(lterm_delete,11,("LTERM deleted\n"));
00826 
00827   return returnCode;
00828 }
00829 
00830 
00831 /* closes all LTERMs (documented in the LTERM interface) */
00832 void lterm_close_all()
00833 {
00834   int lterm;
00835   struct lterms *lts;
00836 
00837   LTERM_LOG(lterm_close_all,10,("\n"));
00838 
00839   GLOBAL_LOCK;
00840 
00841   for (lterm=0; lterm < MAXTERM; lterm++) {
00842     lts = ltermGlobal.termList[lterm];
00843 
00844     if ((lts != NULL) && lts->opened) {
00845       /* Opened LTERM; close it, disregarding return code */
00846       lts->adminMutexLocked = 1;
00847       MUTEX_LOCK(lts->adminMutex);
00848 
00849       /* NOTE,10, ("lterm_close_all:\n"******** This call is being made from within a global lock;
00850        *                POTENTIAL FOR DEADLOCK?
00851        */
00852       ltermClose(lts);
00853 
00854       lts->adminMutexLocked = 0;
00855       MUTEX_UNLOCK(lts->adminMutex);
00856     }
00857   }
00858 
00859   GLOBAL_UNLOCK;
00860 }
00861 
00862 
00864 int lterm_setecho(int lterm, int echo_flag)
00865 {
00866   struct lterms *lts;
00867 
00868   CHECK_IF_VALID_LTERM_NUMBER(lterm_setecho,lterm)
00869 
00870   LTERM_LOG(lterm_setecho,10,("LTERM=%d, echo_flag=%d\n", lterm, echo_flag));
00871 
00872   GLOBAL_LOCK;
00873 
00874   lts = ltermGlobal.termList[lterm];
00875 
00876   if (lts == NULL || !lts->opened || lts->suspended) {
00877     /* LTERM is deleted/closed/suspended */
00878     if (lts == NULL)
00879       LTERM_WARNING("lterm_setecho: Warning - LTERM %d not active\n", lterm);
00880     GLOBAL_UNLOCK;
00881     return -2;
00882   }
00883 
00884   if (lts->shellInitCommands > 0) {
00885     /* send shell initialization string */
00886     if (ltermShellInit(lts,1) != 0) {
00887       GLOBAL_UNLOCK;
00888       return -1;
00889     }
00890   }
00891 
00892   lts->disabledInputEcho = !echo_flag;
00893   lts->restoreInputEcho = 0;
00894 
00895   GLOBAL_UNLOCK;
00896 
00897   return 0;
00898 }
00899 
00900 
00906 int lterm_resize(int lterm, int rows, int cols)
00907 {
00908   struct lterms *lts;
00909   struct LtermOutput *lto;
00910 
00911   CHECK_IF_VALID_LTERM_NUMBER(lterm_resize,lterm)
00912 
00913   LTERM_LOG(lterm_resize,10,("Resizing LTERM=%d, rows=%d, cols=%d\n",
00914                              lterm, rows, cols));
00915 
00916   if ((rows <= 0)  || (cols <= 0))
00917     return -1;
00918 
00919   GLOBAL_LOCK;
00920 
00921   lts = ltermGlobal.termList[lterm];
00922 
00923   if (lts == NULL || !lts->opened || lts->suspended) {
00924     /* LTERM is deleted/closed/suspended */
00925     if (lts == NULL)
00926       LTERM_WARNING("lterm_resize: Warning - LTERM %d not active\n", lterm);
00927     GLOBAL_UNLOCK;
00928     return -2;
00929   }
00930 
00931   if ((rows == lts->nRows) && (cols == lts->nCols)) {
00932     /* Nothing to do */
00933     GLOBAL_UNLOCK;
00934     return 0;
00935   }
00936 
00937   lto = &(lts->ltermOutput);
00938 
00939   LTERM_LOG(lterm_resize,12,("lto->outputMode=%d\n",
00940                              lto->outputMode));
00941 
00942   /* Free full screen buffers */
00943   if (lto->screenChar != NULL)
00944     FREE(lto->screenChar);
00945 
00946   if (lto->screenStyle != NULL)
00947     FREE(lto->screenStyle);
00948 
00949   lto->screenChar  = NULL;
00950   lto->screenStyle = NULL;
00951 
00952   /* Resize screen */
00953   lts->nRows = rows;
00954   lts->nCols = cols;
00955 
00956   if (lto->outputMode == LTERM1_SCREEN_MODE) {
00957     /* Clear screen */
00958     if (ltermClearOutputScreen(lts) != 0)
00959       return -1;
00960   }
00961 
00962   if (lts->ptyMode) {
00963     /* Resize PTY */
00964 #ifndef NO_PTY
00965     if (pty_resize(&(lts->pty), lts->nRows, lts->nCols, 0, 0) != 0) {
00966       GLOBAL_UNLOCK;
00967       return -1;
00968     }
00969 #endif  /* !NO_PTY */
00970   }
00971 
00972   GLOBAL_UNLOCK;
00973 
00974   return 0;
00975 }
00976 
00977 
00986 int lterm_setcursor(int lterm, int row, int col)
00987 {
00988   struct lterms *lts;
00989 
00990   CHECK_IF_VALID_LTERM_NUMBER(lterm_setcursor,lterm)
00991 
00992   LTERM_LOG(lterm_setcursor,10,
00993             ("Setting cursor, LTERM=%d row=%d, col=%d (NOT YET IMPLEMENTED)\n",
00994              lterm, row, col));
00995 
00996   GLOBAL_LOCK;
00997 
00998   lts = ltermGlobal.termList[lterm];
00999 
01000   if (lts == NULL || !lts->opened || lts->suspended) {
01001     /* LTERM is deleted/closed/suspended */
01002     if (lts == NULL)
01003       LTERM_WARNING("lterm_setcursor: Warning - LTERM %d not active\n", lterm);
01004     GLOBAL_UNLOCK;
01005     return -2;
01006   }
01007 
01008   GLOBAL_UNLOCK;
01009 
01010   return 0;
01011 }
01012 
01013 
01015 int lterm_write(int lterm, const UNICHAR *buf, int count, int dataType)
01016 {
01017   struct lterms *lts;
01018   FILEDESC writeBUFFER;
01019   int byteCount, sentCount, packetCount, j;
01020   UNICHAR temLine[PIPEHEADER+MAXCOL], uch;
01021 
01022   CHECK_IF_VALID_LTERM_NUMBER(lterm_write,lterm)
01023 
01024   LTERM_LOG(lterm_write,10,("Writing to LTERM %d\n", lterm));
01025 
01026   GLOBAL_LOCK;
01027 
01028   lts = ltermGlobal.termList[lterm];
01029 
01030   if (lts == NULL || !lts->opened || lts->suspended) {
01031     /* LTERM is deleted/closed/suspended */
01032     if (lts == NULL)
01033       LTERM_WARNING("lterm_write: Warning - LTERM %d not active\n", lterm);
01034     GLOBAL_UNLOCK;
01035     return -2;
01036   }
01037 
01038   assert(VALID_FILEDESC(lts->writeBUFFER));
01039 
01040   /* Save copy of input buffer file descriptor */
01041   writeBUFFER = lts->writeBUFFER;
01042 
01043   if (lts->restoreInputEcho == 1) {
01044     /* Restore TTY echo on input */
01045     lts->restoreInputEcho = 0;
01046     lts->disabledInputEcho = 0;
01047   }
01048 
01049   if (lts->shellInitCommands > 0) {
01050     /* send shell initialization string */
01051     if (ltermShellInit(lts,1) != 0) {
01052       GLOBAL_UNLOCK;
01053       return -1;
01054     }
01055   }
01056 
01057   GLOBAL_UNLOCK;
01058 
01059   assert(count >= 0);
01060 
01061   sentCount = 0;
01062   while (sentCount < count) {
01063     packetCount = count - sentCount;  /* always > 0 */
01064 
01065     if (packetCount > MAXCOLM1)
01066       packetCount = MAXCOLM1;
01067 
01068     if ( (dataType == LTERM_WRITE_PLAIN_INPUT) ||
01069          (dataType == LTERM_WRITE_PLAIN_OUTPUT) ) {
01070       /* Plain text data; break at control characters,
01071        * but break at ESCape character only if not the first character;
01072        * this ensures that escape sequences fit into one record
01073        */
01074 
01075       for (j=0; j<packetCount; j++) {
01076         uch = buf[j+sentCount];
01077         temLine[j+PIPEHEADER] = uch;
01078 
01079         if ((uch < (UNICHAR)U_SPACE) &&
01080             ((uch != (UNICHAR)U_ESCAPE) || (j > 0)))
01081           break;
01082       }
01083 
01084       if (j<packetCount)
01085         packetCount = j+1;
01086 
01087       if ( (packetCount > 1) &&
01088            (temLine[PIPEHEADER+packetCount-1] == U_ESCAPE) ) {
01089         /* Save any terminal ESCape character for next packet, unless alone */
01090         packetCount--;
01091       }
01092 
01093     } else {
01094       /* XML element data (must represent complete element) */
01095       for (j=0; j<packetCount; j++)
01096         temLine[j+PIPEHEADER] = buf[j+sentCount];
01097     }
01098 
01099     byteCount = (PIPEHEADER+packetCount)*sizeof(UNICHAR);
01100 
01101     /* Header contains packet character count and data type */
01102     temLine[0]         = (UNICHAR) packetCount;
01103     temLine[PHDR_TYPE] = (UNICHAR) dataType;
01104 
01105     LTERM_LOGUNICODE(lterm_write,12,(&temLine[PIPEHEADER], packetCount));
01106 
01107     if (WRITE(lts->writeBUFFER,temLine,(SIZE_T)byteCount) != byteCount) {
01108       LTERM_ERROR("lterm_write: Error in writing to input pipe buffer\n");
01109       return -1;
01110     }
01111 
01112     LTERM_LOG(lterm_write,11,
01113               ("wrote %d characters of type %d data to input buffer pipe\n",
01114                packetCount, dataType));
01115 
01116     sentCount += packetCount;
01117   }
01118 
01119   return 0;
01120 }
01121 
01122 
01126 int lterm_read(int lterm, int timeout, UNICHAR *buf, int count,
01127                UNISTYLE *style, int *opcodes, int *opvals,
01128                int *buf_row, int *buf_col, int *cursor_row, int *cursor_col)
01129 {
01130   struct lterms *lts;
01131   int returnCode, temCode;
01132   struct LtermRead ltrStruc;
01133 
01134   CHECK_IF_VALID_LTERM_NUMBER(lterm_read,lterm)
01135 
01136   LTERM_LOG(lterm_read,10,("Reading from LTERM %d\n", lterm));
01137 
01138   GLOBAL_LOCK;
01139 
01140   lts = ltermGlobal.termList[lterm];
01141 
01142   if (lts == NULL || !lts->opened || lts->suspended) {
01143     /* LTERM is deleted/closed/suspended */
01144     if (lts == NULL)
01145       LTERM_WARNING("lterm_read: Warning - LTERM %d not active\n", lterm);
01146 
01147     *opcodes = 0;
01148     *opvals = 0;
01149     *buf_row = 0;
01150     *buf_col = 0;
01151     *cursor_row = 0;
01152     *cursor_col = 0;
01153 
01154     GLOBAL_UNLOCK;
01155     return -2;
01156   }
01157 
01158   SWITCH_GLOBAL_TO_UNILOCK(lterm_read,outputMutex,outputMutexLocked)
01159 
01160   /* Copy "input" parameters to LtermRead structure */
01161   ltrStruc.buf = buf;
01162   ltrStruc.style = style;
01163   ltrStruc.max_count = count;
01164 
01165   /* Call auxiliary function to read data */
01166   temCode = ltermRead(lts, &ltrStruc, timeout);
01167 
01168   if (temCode == 0) {
01169     /* Return count of characters read */
01170     returnCode = ltrStruc.read_count;
01171   } else {
01172     /* Error return */
01173     returnCode = temCode;
01174   }
01175 
01176   /* Copy "output" parameters from LtermRead structure */
01177   *opcodes = ltrStruc.opcodes;
01178   *opvals = ltrStruc.opvals;
01179   *buf_row = ltrStruc.buf_row;
01180   *buf_col = ltrStruc.buf_col;
01181   *cursor_row = ltrStruc.cursor_row;
01182   *cursor_col = ltrStruc.cursor_col;
01183 
01184   if (returnCode == -1) {
01185     /* Error return code; suspend TTY */
01186     LTERM_WARNING("lterm_read: Warning - LTERM %d suspended due to error\n", lterm);
01187     lts->suspended = 1;
01188   }
01189 
01190   RELEASE_UNILOCK(outputMutex,outputMutexLocked)
01191 
01192   GLOBAL_LOCK;
01193   if ((*opcodes != 0) && (lts->shellInitCommands > 0)) {
01194     /* send shell initialization string */
01195     if (ltermShellInit(lts,0) != 0) {
01196       GLOBAL_UNLOCK;
01197       return -1;
01198     }
01199   }
01200   GLOBAL_UNLOCK;
01201 
01202   LTERM_LOG(lterm_read,11,("return code = %d, opcodes=0x%x, opvals=%d\n",
01203                            returnCode, *opcodes, *opvals));
01204 
01205   return returnCode;
01206 }
01207 
01208 
01214 static int ltermShellInit(struct lterms *lts, int all)
01215 {
01216   int shellInitLen, lowCommand, j;
01217 
01218   if (lts->shellInitCommands <= 0)
01219     return 0;
01220 
01221   LTERM_LOG(ltermShellInit,20,("sending shell initialization string\n"));
01222 
01223   if (all) {
01224     /* Send all commands */
01225     lowCommand = 0;
01226   } else {
01227     /* Send single command */
01228     lowCommand = lts->shellInitCommands - 1;
01229   }
01230 
01231   for (j=lts->shellInitCommands-1; j>=lowCommand; j--) {
01232     /* Send shell init command and decrement count */
01233     lts->shellInitCommands--;
01234 
01235     shellInitLen = strlen(lts->shellInitStr[j]);
01236 
01237     if (shellInitLen > 0) {
01238       /* Send shell initialization string */
01239       UNICHAR temLine[PIPEHEADER+MAXCOL];
01240       int remaining, encoded, byteCount;
01241 
01242       utf8toucs(lts->shellInitStr[j], shellInitLen,
01243                 temLine+PIPEHEADER, MAXCOL,
01244                 1, &remaining, &encoded);
01245       if (remaining > 0) {
01246         LTERM_ERROR(
01247          "ltermShellInit: Shell init command %d string too long\n", j+1);
01248         return -1;
01249       }
01250 
01251       temLine[0]         = (UNICHAR) encoded;
01252       temLine[PHDR_TYPE] = (UNICHAR) LTERM_WRITE_PLAIN_INPUT;
01253       byteCount = (PIPEHEADER+encoded)*sizeof(UNICHAR);
01254 
01255       if (WRITE(lts->writeBUFFER, temLine, (SIZE_T)byteCount) != byteCount) {
01256         LTERM_ERROR("ltermShellInit: Error in writing to input pipe buffer\n");
01257         return -1;
01258       }
01259     }
01260   }
01261 
01262   return 0;
01263 }
01264 
01265 
01270 static int ltermCreatePipe(FILEDESC *writePIPE, FILEDESC *readPIPE)
01271 {
01272   FILEDESC pipeFD[2];
01273 
01274   LTERM_LOG(ltermCreatePipe,20,("Creating pipe\n"));
01275 
01276   /* Assume that pipe is unidirectional, although in some UNIX
01277      systems pipes are actually bidirectional */
01278 
01279   if (PIPE(pipeFD) == -1) {
01280     LTERM_ERROR("ltermCreatePipe: Error - pipe creation failed\n");
01281     return -1;
01282   }
01283 
01284   /* Copy pipe file descriptor */
01285   *readPIPE  = pipeFD[0];
01286   *writePIPE = pipeFD[1];
01287 
01288   return 0;
01289 }
01290 
01291 
01300 static int ltermCreateProcess(struct LtermProcess *ltp,
01301                               char *const argv[], int nostderr, int noexport)
01302 {
01303   FILEDESC stdIN, stdOUT, stdERR;
01304 #ifdef USE_NSPR_IO
01305   PRProcessAttr *processAttr;
01306 #else
01307   long child_pid;
01308 #endif
01309 
01310   LTERM_LOG(ltermCreateProcess,20,("Creating process %s, nostderr=%d, noexport=%d\n", argv[0], nostderr, noexport));
01311 
01312   if (nostderr) {
01313     /* No STDERR pipe */
01314     ltp->processERR = NULL_FILEDESC;
01315     stdERR=           NULL_FILEDESC;
01316   } else {
01317     /* Create STDERR pipe: needs clean-up */
01318     FILEDESC pipeFD[2];
01319 
01320     if (PIPE(pipeFD) == -1) {
01321       LTERM_ERROR("ltermCreateProcess: Error - STDERR pipe creation failed\n");
01322       return -1;
01323     }
01324 
01325     /* Copy pipe file descriptor */
01326     ltp->processERR = pipeFD[0];
01327     stdERR = pipeFD[1];
01328   }
01329 
01330   /* Create I/O pipes: needs clean up */
01331   if ( (ltermCreatePipe(&(ltp->processIN), &stdIN) != 0) ||
01332        (ltermCreatePipe(&stdOUT, &(ltp->processOUT)) != 0) )
01333     return -1;
01334 
01335 #ifdef USE_NSPR_IO
01336   /* Create new process attribute structure */
01337   processAttr = PR_NewProcessAttr();
01338 
01339   /* Redirect standard error for process */
01340   if (VALID_FILEDESC(stdERR))
01341     PR_ProcessAttrSetStdioRedirect(processAttr, (PRSpecialFD) 2, stdERR);
01342   else
01343     PR_ProcessAttrSetStdioRedirect(processAttr, (PRSpecialFD) 2, stdOUT);
01344 
01345   /* Redirect standard I/O for process */
01346   PR_ProcessAttrSetStdioRedirect(processAttr, (PRSpecialFD) 0, stdIN);
01347   PR_ProcessAttrSetStdioRedirect(processAttr, (PRSpecialFD) 1, stdOUT);
01348 
01349   /* Create NSPR process; do not export environment */
01350   ltp->processID = PR_CreateProcess(argv[0], argv,
01351                                     (char *const *)0, processAttr);
01352 
01353   if (!VALID_PROCESS(ltp->processID)) {
01354     LTERM_ERROR("ltermCreateProcess: Error in creating NSPR process %s\n",
01355                  argv[0]);
01356     return -1;
01357   }
01358 
01359   LTERM_LOG(ltermCreateProcess,21,("Created NSPR process\n"));
01360 
01361 #else  /* not USE_NSPR_IO */
01362 
01363   /* Fork a child process (FORK) */
01364   child_pid = fork();
01365   if (child_pid < 0) {
01366     LTERM_ERROR("ltermCreateProcess: Error - fork failed\n");
01367     return -1;
01368   }
01369 
01370   LTERM_LOG(ltermCreateProcess,21,("vfork child pid = %d\n", child_pid));
01371 
01372   if (child_pid == 0) {
01373     /* Child process */
01374     int fdMax, fd;
01375 
01376     /* Redirect to specified descriptor or to PTY */
01377     if (VALID_FILEDESC(stdERR)) {
01378       /* Redirect STDERR to specified file descriptor */
01379       if (dup2(stdERR, 2) == -1) {
01380         LTERM_ERROR("ltermCreateProcess: Error - failed dup2 for specified stderr\n");
01381         return -1;
01382       }
01383 
01384     } else {
01385       /* Redirect STDERR to STDOUT pipe */
01386       if (dup2(stdOUT, 2) == -1) {
01387         LTERM_ERROR("ltermCreateProcess: Error - failed dup2 for default stderr\n");
01388         return -1;
01389       }
01390     }
01391 
01392     /* Redirect STDIN and STDOUT to pipes */
01393     if (dup2(stdIN, 0) == -1) {
01394       LTERM_ERROR("ltermCreateProcess: Error - failed dup2 for stdin\n");
01395       return -1;
01396     }
01397 
01398     if (dup2(stdOUT, 1) == -1) {
01399       LTERM_ERROR("ltermCreateProcess: Error - failed dup2 for stdout\n");
01400       return -1;
01401     }
01402 
01403     /* Close all other file descriptors in child process */
01404     fdMax = sysconf(_SC_OPEN_MAX);
01405     for (fd = 3; fd < fdMax; fd++)
01406       close(fd);
01407 
01408     if (argv != NULL) {
01409       /* Execute specified command with arguments */
01410       if (noexport)
01411         execve(argv[0], argv, NULL);
01412       else
01413         execvp(argv[0], argv);
01414 
01415       LTERM_ERROR("ltermCreateProcess: Error in executing command %s\n",
01416                   argv[0]);
01417       return -1;
01418     }
01419   }
01420 
01421   /* Parent process; copy child pid */
01422   ltp->processID = child_pid;
01423 
01424   /* Close child-side pipe end(s) */
01425   if (stdIN != stdOUT)
01426     close(stdIN);
01427 
01428   close(stdOUT);
01429 
01430   /* Close writable end of STDERR pipe in parent process */
01431   if (VALID_FILEDESC(stdERR))
01432     close(stdERR);
01433 
01434 #endif  /* not USE_NSPR_IO */
01435 
01436   LTERM_LOG(ltermCreateProcess,21,("return code = 0\n"));
01437 
01438   return 0;
01439 }
01440 
01443 static void ltermDestroyProcess(struct LtermProcess *ltp)
01444 {
01445 
01446   LTERM_LOG(ltermDestroyProcess,20,("Destroying process\n"));
01447 
01448   if (VALID_PROCESS(ltp->processID)) {
01449     /* Kill process */
01450 #ifdef USE_NSPR_IO
01451     /* **NOTE** Possible memory leak here, because processID is not freed? */
01452     PR_KillProcess(ltp->processID);
01453 #else  /* not USE_NSPR_IO */
01454     LTERM_LOG(ltermDestroyProcess,21,("Killing process %d\n",ltp->processID));
01455     kill(ltp->processID, SIGKILL);
01456 #endif /* not USE_NSPR_IO */
01457 
01458     ltp->processID = NULL_PROCESS;
01459   }
01460 
01461   /* Close process pipes (assumed to be unidirectional) */
01462   if (VALID_FILEDESC(ltp->processERR))
01463     CLOSE(ltp->processERR);
01464 
01465   if (VALID_FILEDESC(ltp->processOUT))
01466     CLOSE(ltp->processOUT);
01467 
01468   if (VALID_FILEDESC(ltp->processIN))
01469     CLOSE(ltp->processIN);
01470 }