home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / c / condor40.zip / CONDOR / src / condor_shadow / shadow.c < prev    next >
C/C++ Source or Header  |  1989-09-06  |  30KB  |  1,190 lines

  1. /* 
  2. ** Copyright 1986, 1987, 1988, 1989 University of Wisconsin
  3. ** 
  4. ** Permission to use, copy, modify, and distribute this software and its
  5. ** documentation for any purpose and without fee is hereby granted,
  6. ** provided that the above copyright notice appear in all copies and that
  7. ** both that copyright notice and this permission notice appear in
  8. ** supporting documentation, and that the name of the University of
  9. ** Wisconsin not be used in advertising or publicity pertaining to
  10. ** distribution of the software without specific, written prior
  11. ** permission.  The University of Wisconsin makes no representations about
  12. ** the suitability of this software for any purpose.  It is provided "as
  13. ** is" without express or implied warranty.
  14. ** 
  15. ** THE UNIVERSITY OF WISCONSIN DISCLAIMS ALL WARRANTIES WITH REGARD TO
  16. ** THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  17. ** FITNESS. IN NO EVENT SHALL THE UNIVERSITY OF WISCONSIN  BE LIABLE FOR
  18. ** ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  19. ** WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  20. ** ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  21. ** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  22. ** 
  23. ** Authors:  Allan Bricker and Michael J. Litzkow,
  24. **              University of Wisconsin, Computer Sciences Dept.
  25. ** 
  26. */ 
  27.  
  28.  
  29. #include <stdio.h>
  30. #include <signal.h>
  31. #include <pwd.h>
  32. #include <netdb.h>
  33. #include <syscall.h>
  34. #include <ctype.h>
  35. #include <sys/types.h>
  36. #include <sys/uio.h>
  37. #include <sys/buf.h>
  38. #include <sys/stat.h>
  39. #include <sys/errno.h>
  40. #include <sys/file.h>
  41. #include <sys/time.h>
  42. #include <sys/param.h>
  43. #include <sys/socket.h>
  44. #include <sys/wait.h>
  45. #include <netinet/in.h>
  46. #include <sys/resource.h>
  47. #include <rpc/types.h>
  48. #include <rpc/xdr.h>
  49. #include "sched.h"
  50. #include "debug.h"
  51. #include "except.h"
  52. #include "trace.h"
  53. #include "fileno.h"
  54. #include "files.h"
  55. #include "proc.h"
  56. #include "expr.h"
  57. #include "exit.h"
  58. #include "condor_types.h"
  59. #include "clib.h"
  60.  
  61. #ifdef NDBM
  62. #include <ndbm.h>
  63. #else NDBM
  64. #include "ndbm_fake.h"
  65. #endif NDBM
  66.  
  67. static char *_FileName_ = __FILE__;        /* Used by EXCEPT (see except.h)     */
  68.  
  69. char     *getenv(), *index(), *strcat(), *strcpy(), *param();
  70. XDR        *xdr_Init();
  71. EXPR    *scan(), *create_expr();
  72. ELEM    *create_elem();
  73. CONTEXT    *create_context(), *build_context();
  74. FILE    *popen();
  75.  
  76. extern int    LockFd;
  77. extern char    MsgBuf[];
  78.  
  79. int        whoami();
  80.  
  81. int        MyPid;
  82. int        MyPipe;
  83. int        LogPipe;
  84. PROC    Proc;
  85. char    *Spool;
  86.  
  87. char    QueueName[MAXPATHLEN];
  88. DBM        *QueueD = NULL, *OpenJobQueue();
  89.  
  90. #define OPENJOBQUEUE(q, name, flags, mode) { \
  91.     if( (q) == NULL ) { \
  92.         (q) = OpenJobQueue(name, flags, mode); \
  93.         if( (q) == NULL ) { \
  94.             EXCEPT("OpenJobQueue(%s)", name); \
  95.         } \
  96.     } \
  97. }
  98.  
  99. #define CLOSEJOBQUEUE(q)    { CloseJobQueue(q); (q) = NULL; }
  100.  
  101. int        CondorGid;
  102. int        CondorUid;
  103. int        ClientUid;
  104. int        ClientGid;
  105.  
  106. struct rusage LocalUsage;
  107. struct rusage RemoteUsage;
  108.  
  109. char    CkptName[ MAXPATHLEN ];        /* The name of the ckpt file */
  110. char    ICkptName[ MAXPATHLEN ];    /* The name of the initial ckpt file */
  111. char    RCkptName[ MAXPATHLEN ];    /* The name of the returned ckpt file */
  112. char    TmpCkptName[ MAXPATHLEN ];    /* The name of the temp ckpt file */
  113.  
  114. char    *sprintf();
  115.  
  116. #ifdef NOTDEF
  117. int        InitialJobStatus = -1;
  118. #endif NOTDEF
  119.  
  120. union wait JobStatus;
  121. struct rusage JobRusage;
  122. int ChildPid;
  123.  
  124. int    DoCleanup();
  125.  
  126. XDR        *xdr_RSC, *RSC_ShadowInit();
  127.  
  128. usage()
  129. {
  130.     dprintf( D_ALWAYS, "Usage: shadow host cluster proc\n" );
  131.     exit( 1 );
  132. }
  133.  
  134. /*ARGSUSED*/
  135. main(argc,argv,envp)
  136. int argc;
  137. char *argv[];
  138. char *envp[];
  139. {
  140.     int p[2];
  141.     int    log[2];
  142.  
  143.     _EXCEPT_Cleanup = DoCleanup;
  144.     MyPid = getpid();
  145.  
  146.     config( argv[0], (CONTEXT *)0 );
  147.  
  148.     dprintf_config( "SHADOW", SHADOW_LOG );
  149.     DebugId = whoami;
  150.  
  151.     dprintf( D_ALWAYS, "********** Shadow Parent starting up **********\n" );
  152.     dprintf( D_ALWAYS, "Server = %s\n", argv[1] );
  153.  
  154.     if( (argc != 4) && (argc != 5) )
  155.         usage();
  156.     
  157.     if( setreuid(0,0) < 0 ) {
  158.         EXCEPT("setreuid(0,0)");
  159.     }
  160.  
  161.     Spool = param( "SPOOL" );
  162.     if( Spool == NULL ) {
  163.         EXCEPT( "Spool directory not specified in config file" );
  164.     }
  165.     (void)sprintf( QueueName, "%s/job_queue", Spool );
  166.  
  167.     start_job( argv[1], argv[2], argv[3] );
  168.  
  169.     if( pipe(p) < 0 ) {
  170.         EXCEPT("Could not establish pipe");
  171.     }
  172.     if( pipe(log) <  0 ) {
  173.         EXCEPT( "Could not extablish log pipe");
  174.     }
  175.  
  176.         /* Don't want to share log file lock between child and pnarent */
  177.     (void)close( LockFd );
  178.     LockFd = -1;
  179.  
  180.     ChildPid = fork();
  181.     if( ChildPid < 0 ) {
  182.         EXCEPT("fork");
  183.     }
  184.  
  185.     if( ChildPid != 0 ) {    /* Parent */
  186.         /*
  187.         ** dprintf( D_ALWAYS, "p[0] = %d, p[1] = %d\n", p[0], p[1] );
  188.         ** dprintf( D_ALWAYS, "log[0] = %d, log[1] = %d\n", log[0], log[1] );
  189.         */
  190.         (void)close( log[1] );
  191.         (void)close( p[1] );
  192.         ParentShadow( p[0], log[0] );
  193.     } else {                /* Child */
  194.  
  195.         _EXCEPT_Cleanup = NULL;    /* Parent will do any needed cleanup */
  196.         (void)close( log[0] );
  197.         (void)close( p[0] );
  198.         MyPipe = p[1];
  199.         LogPipe = log[1];
  200.         MyPid = getpid();
  201.         DebugLock = NULL;
  202.         DebugFile = NULL;
  203.         DebugFP = fdopen( log[1], "w" );
  204.         dprintf( D_ALWAYS, "********** Shadow Child starting up **********\n" );
  205.         HandleSyscalls( p[1] );
  206.     }
  207. }
  208.  
  209. HandleSyscalls( pipe )
  210. int pipe;
  211. {
  212.  
  213.     if( setreuid(0,0) < 0 ) {
  214.         EXCEPT("setreuid(0,0)");
  215.     }
  216.  
  217.  
  218.     dprintf(D_FULLDEBUG, "HandleSyscalls: about to chdir(%s)\n", Proc.rootdir);
  219.     if( chdir(Proc.rootdir) < 0 ) {
  220.         PERM_ERR( "Can't access \"%s\"", Proc.rootdir );
  221.         EXCEPT("chdir(%s)", Proc.rootdir);
  222.     }
  223.  
  224.     dprintf(D_FULLDEBUG, "HandleSyscalls: about to chroot(%s)\n", Proc.rootdir);
  225.     if( chroot(Proc.rootdir) < 0 ) {
  226.         PERM_ERR( "Can't access \"%s\"", Proc.rootdir );
  227.         EXCEPT("chroot(%s)", Proc.rootdir);
  228.     }
  229.  
  230.  
  231.     /*
  232.     **    Set up group array for job owner, but include Condor's gid
  233.     */
  234.     /*
  235.     **    dprintf(D_ALWAYS,"Shadow: about to initgroups(%s, %d)\n",
  236.     **                    Proc.owner,CondorGid);
  237.     */
  238.     if( initgroups(Proc.owner, CondorGid) < 0 ) {
  239.         EXCEPT("Can't initgroups(%s, %d)", Proc.owner, CondorGid);
  240.     }
  241.  
  242.     /*
  243.     ** dprintf(D_ALWAYS,"Shadow: about to setrgid(%d)\n", ClientGid);
  244.     */
  245.         /* Set the rgid to job's owner - keep him out of trouble */
  246.     if( setrgid(ClientGid) < 0 ) {
  247.         EXCEPT( "setrgid(%d)", ClientGid);
  248.     }
  249.  
  250.     /*
  251.     dprintf(D_ALWAYS,"Shadow: about to setruid(%d,%d)\n", ClientUid,ClientUid );
  252.     */
  253.         /* Set both ruid and euid to job's owner - keep him out of trouble */
  254.     if( setreuid(ClientUid,ClientUid) < 0 ) {
  255.         EXCEPT( "setreuid(%d, %d)", ClientUid, ClientUid );
  256.     }
  257.  
  258.     dprintf(D_FULLDEBUG, "HandleSyscalls: about to chdir(%s)\n", Proc.iwd);
  259.     if( chdir(Proc.iwd) < 0 ) {
  260.         PERM_ERR( "Can't access \"%s\"", Proc.iwd );
  261.         EXCEPT("chdir(%s)", Proc.iwd);
  262.     }
  263.  
  264.     dprintf(D_SYSCALLS, "Shadow: Starting to field syscall requests\n");
  265.     errno = 0;
  266.  
  267.     open_std_files( &Proc );
  268.  
  269.     for(;;) {    /* get a request and fulfill it */
  270.         if( do_REMOTE_syscall() < 0 ) {
  271.             dprintf(D_SYSCALLS, "Shadow: do_REMOTE_syscall returned < 0\n");
  272.             break;
  273.         }
  274.     }
  275.  
  276.     {
  277.     int    s;
  278.     errno = 0;
  279.     if( (s=write(pipe, (char *)&JobStatus, sizeof(JobStatus))) !=
  280.                                                         sizeof(JobStatus) ) {
  281.         dprintf( D_ALWAYS, "pipe = %d, status = %d\n", pipe, s );
  282.         EXCEPT("Could not communicate JobStatus to the parent shadow");
  283.     }
  284.     }
  285.  
  286.     if( write(pipe, (char *)&JobRusage, sizeof(JobRusage)) !=
  287.                                                         sizeof(JobRusage) ) {
  288.         EXCEPT("Could not communicate JobRusage to the parent shadow");
  289.     }
  290.     dprintf(D_ALWAYS,
  291.         "Shadow: Job %d.%d exited, termsig = %d, coredump = %d, retcode = %d\n",
  292.             Proc.id.cluster, Proc.id.proc, JobStatus.w_termsig,
  293.             JobStatus.w_coredump, JobStatus.w_retcode );
  294.  
  295.     if( JobStatus.w_termsig == 0 ) {
  296.         dprintf( D_ALWAYS, "********** Shadow Child Exiting (JOB_EXITED)\n" );
  297.         exit( JOB_EXITED );
  298.     }
  299.  
  300.     /*
  301.     **    The checkpoint code will cause the job to exit with a SIGQUIT.
  302.     */
  303.  
  304. /*
  305. **    We should put a description of the situation into
  306. **    (chrooted) /tmp/Shadow.status.pid to be mailed to the
  307. **    user later on.
  308. */
  309.  
  310.     if( JobStatus.w_termsig != SIGQUIT ) {
  311.         if( JobStatus.w_coredump ) {
  312.             char cwd[ MAXPATHLEN ];
  313.             int blen;
  314.  
  315.             if( getwd(cwd) == NULL ) {
  316.                 EXCEPT("getwd returned '%s'", cwd);
  317.             }
  318.  
  319.             blen = strlen(cwd) + 1;
  320.             if( blen == 2 ) {    /* cwd == "/".  Send back a null string */
  321.                 cwd[0] = '\0';
  322.                 blen = 1;
  323.             }
  324.  
  325.             if( write(pipe, cwd, blen) != blen ) {
  326.                 EXCEPT("Could not send core directory to the parent shadow");
  327.             }
  328.             dprintf( D_ALWAYS,
  329.             "********** Shadow Child Exiting (JOB_COREDUMPED)\n" );
  330.             exit( JOB_COREDUMPED );
  331.         } else {
  332.             dprintf( D_ALWAYS,
  333.             "********** Shadow Child Exiting (JOB_KILLED)\n" );
  334.             exit( JOB_KILLED );
  335.         }
  336.     }
  337.  
  338.     dprintf(D_ALWAYS, "Shadow: Job %d.%d exited, new checkpoint was made.\n",
  339.             Proc.id.cluster, Proc.id.proc );
  340.     dprintf( D_ALWAYS, "********** Shadow Child Exiting (JOB_CKPTED)\n" );
  341.     exit( JOB_CKPTED );
  342. }
  343.  
  344. ParentShadow(pipe, log)
  345. int pipe;
  346. int    log;
  347. {
  348.     union wait status;
  349.     struct rusage local_rusage;
  350.     char notification[ BUFSIZ ];
  351.     int pid;
  352.     int    got_status = 0;
  353.     int    got_rusage = 0;
  354.     fd_set    readfds, template;
  355.     int    cnt;
  356.     int    nfds;
  357.     FILE    *log_fp;
  358.     int        rm();
  359.     int        s;
  360.     char coredir[ MAXPATHLEN ];
  361.     int        core_needed;
  362.  
  363.     (void) signal(SIGTERM, rm);
  364.  
  365.     if( (log_fp=fdopen(log,"r")) == NULL ) {
  366.         EXCEPT( "fdopen(%d,'r')", log );
  367.     }
  368.  
  369.     dprintf(D_FULLDEBUG, "Shadow: waiting for JobStatus from %d\n", ChildPid);
  370.     nfds = MAX( pipe, log) + 1;
  371.     FD_ZERO( &template );
  372.     FD_SET( pipe, &template );
  373.     FD_SET( log, &template );
  374.     while( nfds ) {
  375.         readfds = template;
  376.         cnt = select( nfds, (int *)&readfds, (int *)0, (int *)0,
  377.                                                         (struct timeval *)0 );
  378.         if( cnt < 0 ) {
  379.             EXCEPT( "Select(%d,0x%x,0,0,0)", nfds, &readfds );
  380.         }
  381.         if( log >= 0 && FD_ISSET(log,&readfds) ) {
  382.             if( HandleChildLog(log_fp) < 0 ) {
  383.                 FD_CLR(log,&template);
  384.                 (void)close( log );
  385.                 log = -1;
  386.                 nfds = pipe + 1;
  387.             }
  388.             continue;
  389.         }
  390.  
  391.         errno = 0;
  392.         if( !got_status ) {        /* get the job's exit status */
  393.             if( (s=read(pipe, (char *)&JobStatus, sizeof(JobStatus))) !=
  394.                                                         sizeof(JobStatus) ) {
  395.                 dprintf( D_ALWAYS, "pipe = %d, status is %d\n", pipe, s );
  396.                 dprintf( D_ALWAYS,
  397.                     "Could not communicate JobStatus from the child shadow");
  398.                 break;
  399.             }
  400.             if( JobStatus.w_coredump ) {
  401.                 core_needed = 1;
  402.             } else {
  403.                 core_needed = 0;
  404.             }
  405.             got_status = 1;
  406.             dprintf(D_FULLDEBUG, "Shadow: waiting for JobRusage from %d\n",
  407.                                                                     ChildPid);
  408.         } else if( !got_rusage ) {    /* get the rusage */
  409.             if( read(pipe, (char *)&JobRusage, sizeof(JobRusage)) !=
  410.                                                         sizeof(JobRusage) ) {
  411.                 EXCEPT("Could not communicate JobRusage from the child shadow");
  412.             }
  413.             got_rusage = 1;
  414.             if( !core_needed ) {
  415.                 FD_CLR( pipe, &template );
  416.                 (void)close( pipe );
  417.                 pipe = -1;
  418.                 nfds = MAX( pipe, log) + 1;
  419.             }
  420.         } else {        /* get the name of the core directory */
  421.             if( read(pipe, coredir, sizeof(coredir)) < 0 ) {
  422.                 EXCEPT("Could not read core directory");
  423.             }
  424.             FD_CLR( pipe, &template );
  425.             (void)close( pipe );
  426.             pipe = -1;
  427.             nfds = MAX( pipe, log) + 1;
  428.         }
  429.     }
  430.  
  431.     dprintf(D_FULLDEBUG, "Shadow: waiting for %d to exit\n", ChildPid);
  432.     pid = wait3(&status, 0, &local_rusage);
  433.     if( pid < 0 ) {
  434.         EXCEPT("wait3");
  435.     }
  436.  
  437.     if( pid != ChildPid ) {
  438.         EXCEPT("wait3 returned pid %d (not %d)", pid, ChildPid);
  439.     }
  440.  
  441.     dprintf(D_FULLDEBUG, "Shadow: Pid %d returned by wait.\n", pid );
  442.  
  443.     notification[0] = '\0';
  444.     if( got_status ) {
  445.         handle_termination( notification, coredir );
  446.     } else if( MsgBuf[0] ) {/* Child shadow perm err, kill job & notify owner */
  447.         Proc.status = COMPLETED;
  448.         dprintf( D_ALWAYS, "MsgBuf = \"%s\"\n", MsgBuf );
  449.         (void)strcpy( notification, MsgBuf );
  450.     } else {    /* Unexplained death of child shadow, assume temp error */
  451.         dprintf(D_ALWAYS, "Child shadow died, no msg, assuming temp error\n" );
  452.         DoCleanup();
  453.     }
  454.     update_job_status( &local_rusage, &JobRusage );
  455.  
  456.     if( notification[0] ) {
  457.         NotifyUser( notification );
  458.     }
  459.  
  460.     dprintf( D_ALWAYS, "********** Shadow Parent Exiting **********\n" );
  461.     exit( 0 );
  462. }
  463.  
  464. handle_termination( notification, coredir )
  465. char    *notification;
  466. char    *coredir;
  467. {
  468.  
  469.     switch( JobStatus.w_termsig ) {
  470.      case 0: /* If core, bad executable -- otherwise a normal exit */
  471.         if( JobStatus.w_coredump && JobStatus.w_retcode == ENOEXEC ) {
  472.             (void)sprintf( notification, "Job file not executable" );
  473.             dprintf( D_ALWAYS, "Shadow: Job file not executable" );
  474.         } else {
  475.             (void)sprintf(notification, "Remote Unix Job exited with status %d",
  476.                     JobStatus.w_retcode );
  477.             dprintf(D_ALWAYS, "Shadow: Job exited normally with status %d\n",
  478.                 JobStatus.w_retcode );
  479.         }
  480.  
  481.         Proc.status = COMPLETED;
  482.         break;
  483.      case SIGKILL:    /* Kicked off without a checkpoint */
  484.         dprintf(D_ALWAYS, "Shadow: Job was kicked off without a checkpoint\n" );
  485.         DoCleanup();
  486.         break;
  487.      case SIGQUIT:    /* Kicked off, but with a checkpoint */
  488.         dprintf(D_ALWAYS, "Shadow: Job was checkpointed\n" );
  489.         if( strcmp(Proc.rootdir, "/") != 0 ) {
  490.             MvTmpCkpt();
  491.         }
  492.         Proc.status = IDLE;
  493.         break;
  494.      default:    /* Job exited abnormally */
  495.         if( JobStatus.w_coredump ) {
  496.             if( strcmp(Proc.rootdir, "/") == 0 ) {
  497.                 (void)sprintf(notification,
  498.                     "Remote Unix Job %d.%d was killed by signal %d\nCore file is %s/core.%d.%d",
  499.                         Proc.id.cluster, Proc.id.proc, JobStatus.w_termsig,
  500.                         coredir, Proc.id.cluster, Proc.id.proc);
  501.             } else {
  502.                 (void)sprintf(notification,
  503.                     "Remote Unix Job %d.%d was killed by signal %d\nCore file is %s%s/core.%d.%d",
  504.                         Proc.id.cluster, Proc.id.proc, JobStatus.w_termsig,
  505.                         Proc.rootdir, coredir, Proc.id.cluster, Proc.id.proc);
  506.             }
  507.         } else {
  508.             (void)sprintf(notification,
  509.                 "Remote Unix Job %d.%d was killed by signal %d",
  510.                     Proc.id.cluster, Proc.id.proc, JobStatus.w_termsig);
  511.         }
  512.         dprintf(D_ALWAYS, "Shadow: %s\n", notification);
  513.  
  514.         Proc.status = COMPLETED;
  515.         break;
  516.     }
  517. }
  518.  
  519.  
  520. char *
  521. d_format_time( dsecs )
  522. double dsecs;
  523. {
  524.     int days, hours, minutes, secs;
  525.     static char answer[25];
  526.  
  527. #define SECONDS    1
  528. #define MINUTES    (60 * SECONDS)
  529. #define HOURS    (60 * MINUTES)
  530. #define DAYS    (24 * HOURS)
  531.  
  532.     secs = dsecs;
  533.  
  534.     days = secs / DAYS;
  535.     secs %= DAYS;
  536.  
  537.     hours = secs / HOURS;
  538.     secs %= HOURS;
  539.  
  540.     minutes = secs / MINUTES;
  541.     secs %= MINUTES;
  542.  
  543.     (void)sprintf(answer, "%3d %02d:%02d:%02d", days, hours, minutes, secs);
  544.  
  545.     return( answer );
  546. }
  547.  
  548. NotifyUser( buf )
  549. char *buf;
  550. {
  551.     FILE *mailer;
  552.     char cmd[ BUFSIZ ];
  553.     double rutime, rstime, lutime, lstime;    /* remote/local user/sys times */
  554.     double trtime, tltime;    /* Total remote/local time */
  555.  
  556.     switch( Proc.notification ) {
  557.     case NOTIFY_NEVER:
  558.         return;
  559.     case NOTIFY_ALWAYS:
  560.         break;
  561.     case NOTIFY_COMPLETE:
  562.         if( Proc.status == COMPLETED ) {
  563.             break;
  564.         } else {
  565.             return;
  566.         }
  567.     case NOTIFY_ERROR:
  568.         if( (Proc.status == COMPLETED) && (JobStatus.w_retcode != 0) ) {
  569.             break;
  570.         } else {
  571.             return;
  572.         }
  573.     default:
  574.         dprintf(D_ALWAYS, "Condor Job %d.%d has a notification of %d\n",
  575.                 Proc.id.cluster, Proc.id.proc, Proc.notification );
  576.     }
  577.  
  578.     (void)sprintf(cmd, "/bin/mail %s", Proc.owner );
  579.  
  580.     mailer = popen( cmd, "w" );
  581.     if( mailer == NULL ) {
  582.         EXCEPT("Shadow: Cannot do popen(<%s>, <w>", cmd);
  583.     }
  584.  
  585.     fprintf(mailer, "From: Condor\n" );
  586.     fprintf(mailer, "To: %s\n", Proc.owner );
  587.     fprintf(mailer, "Subject: Condor Job %d.%d\n\n",
  588.                                 Proc.id.cluster, Proc.id.proc );
  589.     fprintf(mailer, "%s\n\n", buf );
  590.  
  591.     fprintf(mailer, "Submitted at:        %s", ctime( (time_t *)&Proc.q_date) );
  592.  
  593.     rutime = Proc.remote_usage.ru_utime.tv_sec;
  594.     rstime = Proc.remote_usage.ru_stime.tv_sec;
  595.     trtime = rutime + rstime;
  596.  
  597.     lutime = Proc.local_usage.ru_utime.tv_sec;
  598.     lstime = Proc.local_usage.ru_stime.tv_sec;
  599.     tltime = lutime + lstime;
  600.  
  601.     fprintf(mailer, "Remote User Time:    %s\n", d_format_time(rutime) );
  602.     fprintf(mailer, "Remote System Time:  %s\n", d_format_time(rstime) );
  603.     fprintf(mailer, "Total Remote Time:   %s\n\n", d_format_time(trtime));
  604.     fprintf(mailer, "Local User Time:     %s\n", d_format_time(lutime) );
  605.     fprintf(mailer, "Local System Time:   %s\n", d_format_time(lstime) );
  606.     fprintf(mailer, "Total Local Time:    %s\n\n", d_format_time(tltime));
  607.  
  608.     if( tltime >= 1.0 ) {
  609.         fprintf(mailer, "Leveraging Factor:   %2.1f\n", trtime / tltime );
  610.     }
  611.     
  612.     (void)pclose( mailer );
  613. }
  614.  
  615. update_job_status( localp, remotep )
  616. struct rusage *localp, *remotep;
  617. {
  618.     PROC    proc;
  619.  
  620.     OPENJOBQUEUE(QueueD, QueueName, O_RDWR, 0);
  621.     LockJobQueue( QueueD, WRITER );
  622.  
  623.     proc.id.cluster = Proc.id.cluster;
  624.     proc.id.proc = Proc.id.proc;
  625.     if( FetchProc(QueueD,&proc) < 0 ) {
  626.         EXCEPT( "Shadow: FetchProc(%d.%d)", proc.id.cluster, proc.id.proc );
  627.     }
  628.     if( proc.status == REMOVED ) {
  629.         dprintf( D_ALWAYS, "Job %d.%d has been removed by condor_rm\n",
  630.                                             proc.id.cluster, proc.id.proc );
  631.         if( CkptName[0] != '\0' ) {
  632.             dprintf(D_ALWAYS, "Shadow: unlinking Ckpt '%s'\n", CkptName);
  633.             (void) unlink( CkptName );
  634.         }
  635.         if( TmpCkptName[0] != '\0' ) {
  636.             dprintf(D_ALWAYS, "Shadow: unlinking TmpCkpt '%s'\n", TmpCkptName);
  637.             (void) unlink( TmpCkptName );
  638.         }
  639.     } else {
  640.         update_rusage( &Proc.local_usage, localp );
  641.         update_rusage( &Proc.remote_usage, remotep );
  642.  
  643.         if( StoreProc(QueueD,&Proc) < 0 ) {
  644.             CLOSEJOBQUEUE( QueueD );
  645.             EXCEPT( "StoreProc(%d.%d)", Proc.id.cluster, Proc.id.proc );
  646.         }
  647.         dprintf( D_ALWAYS, "Shadow: marked job status %d\n", Proc.status );
  648.  
  649.         if( Proc.status == COMPLETED ) {
  650.             if( TerminateProc(QueueD,&Proc.id,COMPLETED) < 0 ) {
  651.                 EXCEPT( "TerminateProc(0x%x,%d.%d,COMPLETED)",
  652.                                     QueueD, Proc.id.cluster, Proc.id.proc );
  653.             }
  654.         }
  655.     }
  656.  
  657.     CLOSEJOBQUEUE( QueueD );
  658. }
  659.  
  660. update_rusage( ru1, ru2 )
  661. register struct rusage *ru1, *ru2;
  662. {
  663.     ru1->ru_utime.tv_usec += ru2->ru_utime.tv_usec;
  664.     if( ru1->ru_utime.tv_usec >= 1000000 ) {
  665.         ru1->ru_utime.tv_usec -= 1000000;
  666.         ru1->ru_utime.tv_sec += 1;
  667.     }
  668.     ru1->ru_utime.tv_sec += ru2->ru_utime.tv_sec;
  669.  
  670.     ru1->ru_stime.tv_usec += ru2->ru_stime.tv_usec;
  671.     if( ru1->ru_stime.tv_usec >= 1000000 ) {
  672.         ru1->ru_stime.tv_usec -= 1000000;
  673.         ru1->ru_stime.tv_sec += 1;
  674.     }
  675.     ru1->ru_stime.tv_sec += ru2->ru_stime.tv_sec;
  676.  
  677.     if( ru2->ru_maxrss > ru1->ru_maxrss ) {
  678.         ru1->ru_maxrss = ru2->ru_maxrss;
  679.     }
  680.     if( ru2->ru_ixrss > ru1->ru_ixrss ) {
  681.         ru1->ru_ixrss = ru2->ru_ixrss;
  682.     }
  683.     if( ru2->ru_idrss > ru1->ru_idrss ) {
  684.         ru1->ru_idrss = ru2->ru_idrss;
  685.     }
  686.     if( ru2->ru_isrss > ru1->ru_isrss ) {
  687.         ru1->ru_isrss = ru2->ru_isrss;
  688.     }
  689.     ru1->ru_minflt += ru2->ru_minflt;
  690.     ru1->ru_majflt += ru2->ru_majflt;
  691.     ru1->ru_nswap += ru2->ru_nswap;
  692.     ru1->ru_inblock += ru2->ru_inblock;
  693.     ru1->ru_oublock += ru2->ru_oublock;
  694.     ru1->ru_msgsnd += ru2->ru_msgsnd;
  695.     ru1->ru_msgrcv += ru2->ru_msgrcv;
  696.     ru1->ru_nsignals += ru2->ru_nsignals;
  697.     ru1->ru_nvcsw += ru2->ru_nvcsw;
  698.     ru1->ru_nivcsw += ru2->ru_nivcsw;
  699. }
  700.  
  701. /*
  702. ** Connect to the scheduler on the remote host and ask to run a job.  The
  703. ** connection to the starter will become our main connection to the
  704. ** remote starter, and the scheduler will send back an aux port number to
  705. ** be used for out of band (error) traffic.
  706. */
  707. send_job( proc, host )
  708. PROC    *proc;
  709. char    *host;
  710. {
  711.     int        cmd = START_FRGN_JOB;
  712.     int        reply;
  713.     bool_t    xdr_ports();
  714.     PORTS    ports;
  715.     int        sock;
  716.     XDR        xdr, *xdrs;
  717.     CONTEXT    *context;
  718.  
  719.     dprintf( D_FULLDEBUG, "Shadow: Entering send_job()\n" );
  720.  
  721.     
  722.  
  723.         /* Connect to the startd */
  724.     if( (sock = do_connect(host, "condor_startd", START_PORT)) < 0 ) {
  725.         dprintf( D_ALWAYS, "Shadow: Can't connect to condor_startd on %s\n",
  726.                         host);
  727.         DoCleanup();
  728.         dprintf( D_ALWAYS, "********** Shadow Parent Exiting **********\n" );
  729.         exit( 0 );
  730.     }
  731.     xdrs = xdr_Init( &sock, &xdr );
  732.     xdrs->x_op = XDR_ENCODE;
  733.  
  734.         /* Send the command */
  735.     if( !xdr_int(xdrs, &cmd) ) {
  736.         EXCEPT( "xdr_int()" );
  737.     }
  738.  
  739.         /* Send the job info */
  740.     context = build_context( proc );
  741.     if( !xdr_context(xdrs,context) ) {
  742.         EXCEPT( "xdr_context(0x%x,0x%x)", xdrs, context );
  743.     }
  744.     if( !xdrrec_endofrecord(xdrs,TRUE) ) {
  745.         EXCEPT( "xdrrec_endofrecord(TRUE)" );
  746.     }
  747.     free_context( context );
  748.  
  749.     xdrs->x_op = XDR_DECODE;
  750.     ASSERT( xdr_int(xdrs,&reply) );
  751.     ASSERT( xdrrec_skiprecord(xdrs) );
  752.  
  753.     if( reply != OK ) {
  754.         dprintf( D_ALWAYS, "Shadow: Request to run a job was REFUSED\n" );
  755.         DoCleanup();
  756.         dprintf( D_ALWAYS, "********** Shadow Parent Exiting **********\n" );
  757.         exit( 0 );
  758.     }
  759.     dprintf( D_ALWAYS, "Shadow: Request to run a job was ACCEPTED\n" );
  760.  
  761.     ASSERT( xdr_ports(xdrs,&ports) );
  762.  
  763.     /*
  764.     dprintf( D_ALWAYS, "Shadow: Port1 = %d, Port2 = %d\n",
  765.                                             ports.port1, ports.port2 );
  766.     */
  767.  
  768.     if( ports.port1 == 0 ) {
  769.         dprintf( D_ALWAYS, "Shadow: Request to run a job on %s was REFUSED\n",
  770.                                                                 host );
  771.         dprintf( D_ALWAYS, "********** Shadow Parent Exiting **********\n" );
  772.         exit( 0 );
  773.     }
  774.  
  775.     /*
  776.     **    close(sock);
  777.     */
  778.  
  779.     if( (sock = do_connect(host, (char *)0, (u_short)ports.port1)) < 0 ) {
  780.         EXCEPT( "connect to scheduler on \"%s\", port1 = %d",
  781.                                                     host, ports.port1 );
  782.     }
  783.  
  784.     if( sock != RSC_SOCK ) {
  785.         ASSERT(dup2(sock, RSC_SOCK) >= 0);
  786.         (void)close(sock);
  787.     }
  788.     /*
  789.     dprintf( D_ALWAYS, "Shadow: RSC_SOCK connected, fd = %d\n", RSC_SOCK );
  790.     */
  791.  
  792.     if( (sock = do_connect(host, (char *)0, (u_short)ports.port2)) < 0 ) {
  793.         EXCEPT( "connect to scheduler on \"%s\", port2 = %d",
  794.                                                     host, ports.port2 );
  795.     }
  796.  
  797.     if( sock != CLIENT_LOG ) {
  798.         ASSERT(dup2(sock, CLIENT_LOG) >= 0);
  799.         (void)close(sock);
  800.     }
  801.     /*
  802.     dprintf( D_ALWAYS, "Shadow: CLIENT_LOG connected, fd = %d\n", CLIENT_LOG );
  803.     */
  804.  
  805.     xdr_RSC = RSC_ShadowInit( RSC_SOCK, CLIENT_LOG );
  806.  
  807.     send_job_file( xdr_RSC, proc );
  808. }
  809.  
  810. CONTEXT *
  811. build_context( proc )
  812. PROC    *proc;
  813. {
  814.     char    line[1024];
  815.     CONTEXT    *answer;
  816.  
  817.     answer = create_context();
  818.  
  819.     (void)sprintf( line, "JOB_REQUIREMENTS = (%s) && (Disk >= %d)",
  820.                                 proc->requirements, calc_disk_needed()  );
  821.     store_stmt( scan(line), answer );
  822.  
  823.     if( proc->preferences && proc->preferences[0] ) {
  824.         (void)sprintf( line, "JOB_PREFERENCES = %s", proc->preferences );
  825.     } else {
  826.         (void)sprintf( line, "JOB_PREFERENCES = T" );
  827.     }
  828.     store_stmt( scan(line), answer );
  829.  
  830.     (void)sprintf( line, "Owner = \"%s\"", proc->owner );
  831.     store_stmt( scan(line), answer );
  832.  
  833.     return answer;
  834. }
  835.  
  836. get_client_ids( proc )
  837. register PROC *proc;
  838. {
  839.     struct passwd *pwd;
  840.  
  841.     (void)sprintf( CkptName, "%s/job%06d.ckpt.%d",
  842.             Spool, proc->id.cluster, proc->id.proc );
  843.  
  844.     (void)sprintf( TmpCkptName, "%s.tmp", CkptName );
  845.  
  846.     (void)sprintf( ICkptName, "%s/job%06d.ickpt",
  847.             Spool, proc->id.cluster );
  848.  
  849.     if( strcmp(proc->rootdir, "/") == 0 ) {
  850.         (void)strcpy(RCkptName, CkptName);    /* Return the checkpoint in place */
  851.     } else {
  852.         (void)sprintf( RCkptName, "/job%06d.ckpt.%d",
  853.                 proc->id.cluster, proc->id.proc );
  854.     }
  855.  
  856.     pwd = getpwnam("condor");
  857.     if( pwd == NULL ) {
  858.         EXCEPT("Can't find password entry for user 'condor'");
  859.     }
  860.  
  861.     CondorGid = pwd->pw_gid;
  862.     CondorUid = pwd->pw_uid;
  863.  
  864.     pwd = getpwnam(proc->owner);
  865.     if( pwd == NULL ) {
  866.         EXCEPT("Can't find password entry for '%s'", proc->owner);
  867.     }
  868.  
  869.     ClientUid = pwd->pw_uid;
  870.     ClientGid = pwd->pw_gid;
  871. }
  872.  
  873. send_job_file( xdrs, proc )
  874. XDR        *xdrs;
  875. PROC    *proc;
  876. {
  877.     int        in_fd;
  878.     int        len;
  879.     struct stat st_buf;
  880.     char    buf[ XDR_BLOCKSIZ ];
  881.     int        ask, received;
  882.     char    *return_name;
  883.  
  884.     dprintf( D_ALWAYS, "Shadow: send_job_file( 0x%x, %d.%d, %s, %s, %s, %s )\n",
  885.         xdrs, proc->id.cluster, proc->id.proc,
  886.         proc->in, proc->out, proc->err, proc->args );
  887.     
  888.     dprintf(D_FULLDEBUG, "Shadow: send_job_file: RootDir = <%s>\n",
  889.                                                             proc->rootdir);
  890.  
  891.     in_fd = open( CkptName, O_RDONLY, 0 );
  892.     if( in_fd < 0 ) {
  893.         in_fd = open( ICkptName, O_RDONLY, 0 );
  894.     }
  895.     if( in_fd < 0 ) {
  896.         EXCEPT( "open(\"%s\",O_RDONLY,0)", ICkptName );
  897.     }
  898.  
  899.     if( fstat(in_fd,&st_buf) < 0 ) {
  900.         EXCEPT( "fstat" );
  901.     }
  902.     len = st_buf.st_size;
  903.  
  904. #ifdef DAYAO
  905.     StartRecording();
  906. #endif DAYAO
  907.  
  908.     xdrs->x_op = XDR_ENCODE;
  909.     dprintf( D_FULLDEBUG, "Shadow: Sending proc structure\n" );
  910.     ASSERT(xdr_proc(xdrs, proc));
  911.  
  912.     return_name = RCkptName;
  913.     dprintf( D_FULLDEBUG, "Shadow: Sending name = '%s'\n", return_name );
  914.     ASSERT( xdr_string(xdrs, &return_name, (u_int) MAXPATHLEN) );
  915.  
  916.     dprintf( D_FULLDEBUG, "Shadow: Sending len = %d\n", len );
  917.     ASSERT( xdr_int(xdrs, &len) );
  918.     
  919.     while( len ) {
  920.         ask = len < XDR_BLOCKSIZ ? len : XDR_BLOCKSIZ;
  921.  
  922.         if( (received=read(in_fd,buf,ask)) < 0 ) {
  923.             EXCEPT( "read" );
  924.         }
  925.  
  926.         ASSERT( xdr_opaque(xdrs, buf, (u_int)received) );
  927.  
  928.         len -= received;
  929.     }
  930.  
  931.     ASSERT( xdrrec_endofrecord(xdrs, TRUE) );
  932.  
  933. #ifdef DAYAO
  934.     CompleteRecording( st_buf.st_size );
  935. #endif DAYAO
  936.  
  937.     dprintf( D_ALWAYS, "Shadow: Done sending job file\n" );
  938.  
  939.     (void)close( in_fd );
  940. }
  941.  
  942.  
  943. extern char    *SigNames[];
  944.  
  945. rm()
  946. {
  947.     dprintf( D_ALWAYS, "Shadow: Got RM command *****\n" );
  948.  
  949.     if( ChildPid ) {
  950.         (void) kill( ChildPid, SIGKILL );
  951.     }
  952.  
  953.     if( strcmp(Proc.rootdir, "/") != 0 ) {
  954.         (void)sprintf( TmpCkptName, "%s/job%06d.ckpt.%d",
  955.                     Proc.rootdir, Proc.id.cluster, Proc.id.proc );
  956.     }
  957.  
  958.     (void) unlink( TmpCkptName );
  959.     (void) unlink( CkptName );
  960.  
  961.     exit( 1 );
  962. }
  963.  
  964.  
  965. /*
  966. ** Print an identifier saying who we are.  This function gets handed to
  967. ** dprintf().
  968. */
  969. whoami( fp )
  970. FILE *fp;
  971. {
  972.     if( Proc.id.cluster || Proc.id.proc ) {
  973.         fprintf( fp, "(%d.%d) (%d):", Proc.id.cluster, Proc.id.proc, MyPid );
  974.     } else {
  975.         fprintf( fp, "(?.?) (%d):", MyPid );
  976.     }
  977. }
  978.  
  979. /*
  980. ** Opens job queue (Q), and reads in process structure (Proc) as side
  981. ** affects.
  982. */
  983. start_job( host, cluster_id, proc_id )
  984. char    *host;
  985. char    *cluster_id;
  986. char    *proc_id;
  987. {
  988.     OPENJOBQUEUE(QueueD, QueueName, O_RDWR, 0);
  989.     LockJobQueue( QueueD, WRITER );
  990.  
  991.     Proc.id.cluster = atoi( cluster_id );
  992.     Proc.id.proc = atoi( proc_id );
  993.  
  994.     if( FetchProc(QueueD,&Proc) < 0 ) {
  995.         EXCEPT( "FetchProc(%d.%d)", Proc.id.cluster, Proc.id.proc );
  996.     }
  997.  
  998.     if( Proc.status != RUNNING ) {
  999.         dprintf( D_ALWAYS, "Shadow: Asked to run proc %d.%d, but status = %d\n",
  1000.                             Proc.id.cluster, Proc.id.proc, Proc.status );
  1001.         dprintf(D_ALWAYS, "********** Shadow Parent Exiting **********\n" );
  1002.         exit( 0 );    /* don't cleanup here */
  1003.     }
  1004.  
  1005.     LocalUsage = Proc.local_usage;
  1006.     RemoteUsage = Proc.remote_usage;
  1007.  
  1008.     CLOSEJOBQUEUE( QueueD );
  1009.     get_client_ids( &Proc );
  1010.     send_job( &Proc, host );
  1011. }
  1012.  
  1013. DoCleanup()
  1014. {
  1015.     char    ckpt_name[MAXPATHLEN];
  1016.  
  1017.     dprintf( D_FULLDEBUG, "Shadow: Entered DoCleanup()\n" );
  1018.  
  1019.     if( TmpCkptName[0] != '\0' ) {
  1020.         dprintf(D_ALWAYS, "Shadow: DoCleanup: unlinking TmpCkpt '%s'\n",
  1021.                                                             TmpCkptName);
  1022.         (void) unlink( TmpCkptName );
  1023.     }
  1024.  
  1025.     if( Proc.id.cluster ) {
  1026.         if( QueueD == NULL ) {
  1027.             if( (QueueD=OpenJobQueue(QueueName, O_RDWR, 0)) == NULL ) {
  1028.                 dprintf( D_ALWAYS,"Shadow: OpenJobQUeue(%s) failed, errno %d\n",
  1029.                                                             QueueName, errno );
  1030.                 dprintf( D_ALWAYS,
  1031.                 "********** Shadow Parent Exiting **********\n" );
  1032.                 exit( 1 );
  1033.             }
  1034.         }
  1035.         LockJobQueue( QueueD, WRITER );
  1036.  
  1037.         if( FetchProc(QueueD,&Proc) < 0 ) {
  1038.             dprintf(D_ALWAYS, "Job %d.%d has been removed by condor_rm\n",
  1039.                                             Proc.id.cluster, Proc.id.proc );
  1040.             dprintf(D_ALWAYS, "Shadow: unlinking Ckpt '%s'\n", ckpt_name);
  1041.             (void) unlink( ckpt_name );
  1042.             dprintf( D_ALWAYS,"********** Shadow Parent Exiting **********\n" );
  1043.             exit( 1 );
  1044.         }
  1045.  
  1046.         (void)sprintf( ckpt_name, "%s/job%06d.ckpt.%d",
  1047.                                     Spool, Proc.id.cluster, Proc.id.proc  );
  1048.  
  1049.         if( Proc.status == REMOVED ) {
  1050.             dprintf(D_ALWAYS, "Job %d.%d has been removed by condor_rm\n",
  1051.                                             Proc.id.cluster, Proc.id.proc );
  1052.             dprintf(D_ALWAYS, "Shadow: unlinking Ckpt '%s'\n", ckpt_name);
  1053.             (void) unlink( ckpt_name );
  1054.             CLOSEJOBQUEUE( QueueD );
  1055.             return;
  1056.         }
  1057.             
  1058.         if( access(ckpt_name,F_OK) != 0 ) {
  1059.             Proc.status = UNEXPANDED;
  1060.         } else {
  1061.             Proc.status = IDLE;
  1062.         }
  1063.  
  1064.         if( StoreProc(QueueD,&Proc) < 0 ) {
  1065.             dprintf( D_ALWAYS, "Shadow: StoreProc(%d.%d) failed, errno = %d",
  1066.                                     Proc.id.cluster, Proc.id.proc, errno );
  1067.             dprintf(D_ALWAYS, "********** Shadow Parent Exiting **********\n" );
  1068.             exit( 1 );
  1069.         }
  1070.         CLOSEJOBQUEUE( QueueD );
  1071.         dprintf( D_ALWAYS, "Shadow: marked job status %d\n", Proc.status );
  1072.     }
  1073. }
  1074.  
  1075. MvTmpCkpt()
  1076. {
  1077.     char buf[ BUFSIZ * 8 ];
  1078.     register int tfd, rfd, rcnt, wcnt;
  1079.  
  1080.     if( setegid(CondorGid) < 0 ) {
  1081.         EXCEPT("Cannot setegid(%d)", CondorGid);
  1082.     }
  1083.  
  1084.     if( seteuid(ClientUid) < 0 ) {
  1085.         EXCEPT("Cannot seteuid(%d)", ClientUid);
  1086.     }
  1087.  
  1088.     (void)sprintf( TmpCkptName, "%s/job%06d.ckpt.%d.tmp",
  1089.             Spool, Proc.id.cluster, Proc.id.proc );
  1090.  
  1091.     (void)sprintf( RCkptName, "%s/job%06d.ckpt.%d",
  1092.                 Proc.rootdir, Proc.id.cluster, Proc.id.proc );
  1093.  
  1094.     rfd = open( RCkptName, O_RDONLY, 0 );
  1095.     if( rfd < 0 ) {
  1096.         EXCEPT(RCkptName);
  1097.     }
  1098.  
  1099.     tfd = open(TmpCkptName, O_WRONLY|O_CREAT|O_TRUNC, 0775);
  1100.     if( tfd < 0 ) {
  1101.         EXCEPT(TmpCkptName);
  1102.     }
  1103.  
  1104.     for(;;) {
  1105.         rcnt = read( rfd, buf, sizeof(buf) );
  1106.         if( rcnt < 0 ) {
  1107.             EXCEPT("read <%s>", RCkptName);
  1108.         }
  1109.  
  1110.         wcnt = write( tfd, buf, rcnt );
  1111.         if( wcnt != rcnt ) {
  1112.             EXCEPT("wcnt %d != rcnt %d", wcnt, rcnt);
  1113.         }
  1114.  
  1115.         if( rcnt != sizeof(buf) ) {
  1116.             break;
  1117.         }
  1118.     }
  1119.  
  1120.     (void)unlink( RCkptName );
  1121.  
  1122.     if( rename(TmpCkptName, CkptName) < 0 ) {
  1123.         EXCEPT("rename(%s, %s)", TmpCkptName, CkptName);
  1124.     }
  1125.  
  1126.     (void)fchown( tfd, CondorUid, CondorGid );
  1127.  
  1128.     (void)close( rfd );
  1129.     (void)close( tfd );
  1130. }
  1131.  
  1132. SetSyscalls(){}
  1133.  
  1134. calc_disk_needed( )
  1135. {
  1136.     struct stat    buf;
  1137.  
  1138.     if( stat(CkptName,&buf) == 0 ) {
  1139.         return buf.st_size / 1024;
  1140.     }
  1141.     if( stat(ICkptName,&buf) == 0 ) {
  1142.         return buf.st_size / 1024;
  1143.     }
  1144.     EXCEPT( "Can't stat checkpoint file \"%s\"", ICkptName );
  1145. #ifdef LINT
  1146.     return -1;
  1147. #endif LINT
  1148. }
  1149.  
  1150. open_std_files( proc )
  1151. PROC    *proc;
  1152. {
  1153.     int        fd;
  1154.  
  1155.     dprintf( D_FULLDEBUG, "Entered open_std_files()\n" );
  1156.  
  1157.     (void)close( 0 );
  1158.     (void)close( 1 );
  1159.     (void)close( 2 );
  1160.  
  1161.     if( (fd=open(proc->in,O_RDONLY,0)) < 0 ) {
  1162.         PERM_ERR( "Can't open \"%s\"", proc->in );
  1163.         EXCEPT( "open(%s,O_RDONLY,0)", proc->in );
  1164.     }
  1165.     if( fd != 0 ) {
  1166.         EXCEPT( "open returns %d, expected 0", fd );
  1167.     }
  1168.  
  1169.     if( (fd=open(proc->out,O_WRONLY,0)) < 0 ) {
  1170.         PERM_ERR( "Can't open \"%s\"", proc->out );
  1171.         EXCEPT( "open(%s,O_WRONLY,0)", proc->out );
  1172.     }
  1173.     if( fd != 1 ) {
  1174.         dprintf( D_ALWAYS, "Can't open \"%s\"\n", proc->err );
  1175.         EXCEPT( "open returns %d, expected 1", fd );
  1176.     }
  1177.  
  1178.  
  1179.     if( (fd=open(proc->err,O_WRONLY,0)) < 0 ) {
  1180.         PERM_ERR( "Can't open \"%s\"", proc->err );
  1181.         EXCEPT( "open(%s,O_WRONLY,0)", proc->err );
  1182.     }
  1183.     if( fd != 2 ) {
  1184.         EXCEPT( "open returns %d, expected 2", fd );
  1185.     }
  1186.  
  1187.     dprintf( D_ALWAYS, "Opened \"%s\", \"%s\", and \"%s\"\n",
  1188.                     proc->in, proc->out, proc->err );
  1189. }
  1190.