Main Page | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

rktimes.c

Go to the documentation of this file.
00001 /*
00002  * rktimes.c
00003  *
00004  * Copyright (c) 2003, 2004 The University of Utah and the Flux Group.
00005  * All rights reserved.
00006  *
00007  * This file is licensed under the terms of the GNU Public License.  
00008  * See the file "license.terms" for restrictions on redistribution 
00009  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
00010  */
00011 
00012 /**
00013  * @file rktimes.c
00014  *
00015  * Main file for a sampling time(1)-like command.  Unlike time(1) though, it
00016  * will not wait until the end of a run to report the CPU usage, it
00017  * continuously reports the usage in a form fit for consumption by gnuplot.
00018  * In addition, it can act as a server for a GKrellm client so the data can be
00019  * viewed instantaneously in a nice GUI.
00020  */
00021 
00022 #include "config.h"
00023 
00024 #include <errno.h>
00025 #include <stdio.h>
00026 #include <string.h>
00027 #include <stdlib.h>
00028 #include <signal.h>
00029 #include <unistd.h>
00030 #include <fcntl.h>
00031 
00032 #include <sys/time.h>
00033 #include <sys/types.h>
00034 #include <sys/times.h>
00035 #include <sys/wait.h>
00036 #include <sys/socket.h>
00037 #include <sys/resource.h>
00038 
00039 #include <netinet/in.h>
00040 
00041 #include <assert_pp.h>
00042 #include <time_util.h>
00043 
00044 #include "gkemu.h"
00045 #include "childProcess.h"
00046 
00047 #include <rk/rk.h>
00048 #include <rk/rk_error.h>
00049 
00050 #include "rk_util.h"
00051 
00052 /**
00053  * Number of times per second to record CPU usage samples.
00054  */
00055 #define SAMPLES_PER_SECOND 1
00056 
00057 /**
00058  * Maximum number of gkrellm clients.
00059  */
00060 #define MAX_CLIENTS 8
00061 
00062 /**
00063  * Size of the buffer used to format gkrellm output.
00064  */
00065 #define CLIENT_BUFFER_MAX 4096
00066 
00067 #if !defined(__XSTRING)
00068 /**
00069  * Convert a macro argument to a string.
00070  *
00071  * @param x The macro to expand to a string.
00072  */
00073 #define __XSTRING(x) __STRING(x)
00074 #endif
00075 
00076 /**
00077  * A version of perror(3) that prints out the file and line number.
00078  *
00079  * @param x The prefix string for the error string.
00080  */
00081 #define my_perror(x) perror(x ", file " __FILE__ ", line " __XSTRING(__LINE__))
00082 
00083 enum {
00084     RKTB_DONE,
00085     RKTB_CREATED_RESOURCE_SET,
00086 };
00087 
00088 /*
00089  * Flags for the rktimes_data.rkt_Flags field.
00090  *
00091  * RKTF_DONE - Stop monitoring the resource set/child process.
00092  * RKTF_CREATED_RESOURCE_SET - rktimes created the resource set for the child.
00093  */
00094 enum {
00095     RKTF_DONE = (1L << RKTB_DONE),
00096     RKTF_CREATED_RESOURCE_SET = (1L << RKTB_CREATED_RESOURCE_SET),
00097 };
00098 
00099 enum {
00100     RKTB_CLIENT_INITIALIZED,
00101 };
00102 
00103 /*
00104  * Flags for the rktimes_data.rkt_ClientFlags fields.
00105  *
00106  * RKTF_CLIENT_INITIALIZED - Indicates that the client has been sent an
00107  * "initial update".
00108  */
00109 enum {
00110     RKTF_CLIENT_INITIALIZED = (1L << RKTB_CLIENT_INITIALIZED),
00111 };
00112 
00113 /*
00114  * Global data for the tool.
00115  *
00116  * rkt_Flags - Holds the RKTF_ flags.
00117  * rkt_UtilityName - The name of the utility that is being monitored.
00118  * rkt_StartTime - The starting time for the child process.
00119  * rkt_OutputBase - The base name for output files.
00120  * rkt_Name - The requested name of the resource set.
00121  * rkt_ChildPeriod - The child's CPU reservation period or zero if a reserve
00122  * should not be created.
00123  * rkt_ChildCompute - The child's CPU reservation compute time.
00124  * rkt_ResourceSet - The child's resource set.
00125  * rkt_ChildPID - The pid_t of the child process.  This will be set to -1 when
00126  *   the child dies.
00127  * rkt_ServerSocket - The server socket listening for gkrellm clients.
00128  * rkt_ClientBuffer - Work buffer for sending data to gkrellm clients.
00129  * rkt_ClientSockets - A list of socket descriptors that correspond to gkrellm
00130  *   clients listening for updates.  Elements set to -1 are empty.
00131  */
00132 static struct {
00133     unsigned long rkt_Flags;
00134     const char *rkt_UtilityName;
00135     struct timeval rkt_StartTime;
00136     const char *rkt_OutputBase;
00137     const char *rkt_Name;
00138     unsigned int rkt_ChildPeriod;
00139     unsigned int rkt_ChildCompute;
00140     rk_resource_set_t rkt_ResourceSet;
00141     pid_t rkt_ChildPID;
00142     
00143     int rkt_ServerSocket;
00144     char rkt_ClientBuffer[CLIENT_BUFFER_MAX];
00145     int rkt_ClientSockets[MAX_CLIENTS];
00146     unsigned long rkt_ClientFlags[MAX_CLIENTS];
00147 } rktimes_data;
00148 
00149 /**
00150  * Write a byte array to a client's socket.  If they happen to fall behind or
00151  * some other error is encountered, the socket is closed immediately and erased
00152  * from the rktimes_data.rkt_ClientSockets array.
00153  *
00154  * @param index The client index in rktimes_data.rkt_ClientSockets.
00155  * @param buffer The buffer to write to the socket.
00156  * @param len The length of the buffer.
00157  */
00158 static void rktClientWrite(unsigned int index, const char *buffer, size_t len)
00159 {
00160     int fd;
00161 
00162     require(rktimes_data.rkt_ServerSocket != -1);
00163     require(index < MAX_CLIENTS);
00164     require(buffer != NULL);
00165 
00166     if( (fd = rktimes_data.rkt_ClientSockets[index]) >= 0 )
00167     {
00168         int rc;
00169 
00170         rc = write(fd, buffer, len);
00171         if( rc != len )
00172         {
00173             require((rc != -1) || (errno == EPIPE) || (errno == EWOULDBLOCK));
00174 
00175             /* No mercy! */
00176             close(fd);
00177             rktimes_data.rkt_ClientSockets[index] = -1;
00178             rktimes_data.rkt_ClientFlags[index] = 0;
00179         }
00180     }
00181 }
00182 
00183 /**
00184  * A signal handler that passes the received signal on to the child process.
00185  *
00186  * @param sig The actual signal number received.
00187  */
00188 static void sigpass(int sig)
00189 {
00190     require((sig == SIGINT) || (sig == SIGTERM));
00191     require(rktimes_data.rkt_ChildPID != 0);
00192 
00193     if( kill(rktimes_data.rkt_ChildPID, sig) == -1 )
00194     {
00195         my_perror("kill");
00196     }
00197 }
00198 
00199 /** Maximum number of processes to query in the resource set. */
00200 #define MAX_PROC_LIST 128
00201 
00202 /**
00203  * The SIGALRM signal handler.  This function will be called SAMPLES_PER_SECOND
00204  * times a second to read the CPU usage of the resource set's processes.  The
00205  * data is then output to a file and echoed to any gkrellm clients.
00206  *
00207  * @param sig The actual signal number received.
00208  *
00209  * @sa gkemu.h
00210  * @sa childProcess.h
00211  */
00212 static void sigalrm(int sig)
00213 {
00214     static unsigned long long total_compute, total_nice, total_deadline;
00215     static unsigned long long total_usage;
00216     static pid_t procs[MAX_PROC_LIST];
00217 
00218     unsigned long long inc_usage = 0, total_run, total_idle;
00219     struct cpu_reserve_attr cra;
00220     struct timeval curr, run;
00221     int lpc, len, count;
00222     rk_reserve_t cr;
00223 
00224     require(sig == SIGALRM);
00225 
00226     /* Get the current time and compute the process' total running time. */
00227     gettimeofday(&curr, NULL);
00228     timersub(&curr, &rktimes_data.rkt_StartTime, &run);
00229     total_run = (run.tv_sec * 1000000) + run.tv_usec;
00230 
00231     /* Get the list of processes in the resource set. */
00232     if( (count = rk_resource_set_get_proclist(rktimes_data.rkt_ResourceSet,
00233                                               procs,
00234                                               MAX_PROC_LIST)) == -1 )
00235     {
00236         my_perror("rk_resource_set_get_proclist");
00237         
00238         rktimes_data.rkt_Flags |= RKTF_DONE;
00239     }
00240 
00241     /* Walk through each process in the resource set and update its usage. */
00242     for( lpc = 0; lpc < count; lpc++ )
00243     {
00244         struct cpChildProcess *cp;
00245         
00246         if( (cp = cpFindChildProcess(procs[lpc])) == NULL )
00247         {
00248             /* The child is new, create an object for it. */
00249             if( (cp = cpCreateChildProcess(procs[lpc])) != NULL )
00250             {
00251                 cpOpenOutput(cp,
00252                              rktimes_data.rkt_OutputBase,
00253                              &rktimes_data.rkt_StartTime);
00254             }
00255         }
00256         if( cp != NULL )
00257         {
00258             inc_usage += cpSampleUsage(cp, &run);
00259         }
00260     }
00261 
00262     /* Add the usage since the last time this handler was called. */
00263     total_usage += inc_usage;
00264 
00265     /* Get the current CPU reservation status. */
00266     if( (cr = rk_resource_set_get_cpu_rsv(rktimes_data.rkt_ResourceSet)) !=
00267         NULL_RESERVE)
00268     {
00269         if( rk_cpu_reserve_get_attr(cr, &cra) != RK_ERROR )
00270         {
00271             unsigned long long rsv_period, rsv_compute, rsv_nice, rsv_deadline;
00272             double multiplier;
00273 
00274             /* Turn the raw reservation data into a form fit for gkrellm. */
00275             rsv_period = (cra.period.tv_sec * 1000000) +
00276                 (cra.period.tv_nsec / 1000);
00277             multiplier = (1000000.0 / rsv_period) /
00278                 (double)SAMPLES_PER_SECOND;
00279             rsv_compute = ((cra.compute_time.tv_sec * 1000000) +
00280                            (cra.compute_time.tv_nsec / 1000)) * multiplier;
00281             if( inc_usage > rsv_compute )
00282             {
00283                 rsv_nice = 0;
00284             }
00285             else
00286             {
00287                 rsv_nice = rsv_compute - inc_usage;
00288                 rsv_compute = inc_usage;
00289             }
00290             rsv_deadline = ((cra.deadline.tv_sec * 1000000) +
00291                             (cra.deadline.tv_nsec / 1000)) * multiplier;
00292 
00293             total_deadline += rsv_deadline - (rsv_compute + rsv_nice);
00294             total_compute += rsv_compute;
00295             total_nice += rsv_nice;
00296         }
00297         else
00298         {
00299             my_perror("rk_cpu_reserve_get_attr");
00300         }
00301     }
00302     
00303     total_idle = total_run - total_usage;
00304 
00305     /* Generate the new update text. */
00306     len = gkFormatUpdate(rktimes_data.rkt_ClientBuffer, CLIENT_BUFFER_MAX,
00307                          GKA_CPUUser, total_usage,
00308                          GKA_CPUIdle, total_idle,
00309                          GKA_CPUReserveUser, total_compute,
00310                          GKA_CPUReserveNice, total_nice,
00311                          GKA_CPUReserveIdle, total_deadline,
00312                          GKA_Processes, count,
00313                          GKA_ProcessesRunning, 0,
00314                          GKA_UpTime, &run,
00315                          GKA_TAG_DONE);
00316 
00317     /* Broadcast the update to the clients. */
00318     for( lpc = 0; lpc < MAX_CLIENTS; lpc++ )
00319     {
00320         if( rktimes_data.rkt_ClientSockets[lpc] != -1 )
00321         {
00322             if( !(rktimes_data.rkt_ClientFlags[lpc] &
00323                   RKTF_CLIENT_INITIALIZED) )
00324             {
00325                 rktClientWrite(lpc,
00326                                gkInitialUpdateOpen,
00327                                strlen(gkInitialUpdateOpen));
00328             }
00329             rktClientWrite(lpc, rktimes_data.rkt_ClientBuffer, len);
00330             if( !(rktimes_data.rkt_ClientFlags[lpc] &
00331                   RKTF_CLIENT_INITIALIZED) )
00332             {
00333                 rktClientWrite(lpc,
00334                                gkInitialUpdateClose,
00335                                strlen(gkInitialUpdateClose));
00336                 rktimes_data.rkt_ClientFlags[lpc] |= RKTF_CLIENT_INITIALIZED;
00337             }
00338         }
00339     }
00340 }
00341 
00342 /**
00343  * Handle a SIGCHLD signal.  This function will be called when the child exits.
00344  *
00345  * @param sig The actual signal number received.
00346  */
00347 static void sigchld(int sig)
00348 {
00349     require(sig == SIGCHLD);
00350     require(rktimes_data.rkt_ChildPID != 0);
00351     
00352     rktimes_data.rkt_Flags |= RKTF_DONE;
00353 }
00354 
00355 /**
00356  * Handle a SIGINT/SIGTERM signal when no utility is being monitored.
00357  *
00358  * @param sig The actual signal number received.
00359  */
00360 static void sigexit(int sig)
00361 {
00362     require((sig == SIGINT) || (sig == SIGTERM));
00363     
00364     rktimes_data.rkt_Flags |= RKTF_DONE;
00365 }
00366 
00367 /**
00368  * Handle a SIGIO signal.  This function will be called when the server socket
00369  * has a new connection waiting to be accept(2)'d.
00370  *
00371  * @param sig The actual signal number received.
00372  */
00373 static void sigio(int sig)
00374 {
00375     struct sockaddr saddr;
00376     socklen_t saddrlen;
00377     int fd;
00378 
00379     require(sig == SIGIO);
00380     require(rktimes_data.rkt_ServerSocket != -1);
00381 
00382     saddrlen = sizeof(saddr);
00383     /* Try to accept all of the clients waiting on the socket. */
00384     while( (fd = accept(rktimes_data.rkt_ServerSocket,
00385                         &saddr,
00386                         &saddrlen)) >= 0 )
00387     {
00388         int fl, lpc;
00389 
00390         /*
00391          * Set the client socket to be non-blocking, so we do not want to get
00392          * stuck waiting for someone.
00393          */
00394         fl = fcntl(fd, F_GETFL, 0);
00395         fcntl(fd, F_SETFL, fl | O_NONBLOCK);
00396         /* Search for an empty element in the list of client sockets. */
00397         for( lpc = 0;
00398              (lpc < MAX_CLIENTS) &&
00399                  (rktimes_data.rkt_ClientSockets[lpc] != -1);
00400              lpc++ );
00401         if( lpc < MAX_CLIENTS )
00402         {
00403             int len;
00404 
00405             rktimes_data.rkt_ClientSockets[lpc] = fd;
00406 
00407             /* Send the preamble. */
00408             len = gkFormatPreamble(
00409                 rktimes_data.rkt_ClientBuffer,
00410                 CLIENT_BUFFER_MAX,
00411                 GKA_HostName, rktimes_data.rkt_Name,
00412                 GKA_SystemName, rktimes_data.rkt_UtilityName,
00413                 GKA_TAG_DONE);
00414             rktClientWrite(lpc, rktimes_data.rkt_ClientBuffer, len);
00415         }
00416         else
00417         {
00418             /* Too many clients... */
00419             close(fd);
00420         }
00421     }
00422 }
00423 
00424 /**
00425  * Create a resource set and CPU reserve for ourself.  Otherwise, we might not
00426  * get CPU time to do the recording.
00427  *
00428  * @param name The name for the resource set.
00429  * @return A resource set for this process.
00430  */
00431 static rk_resource_set_t rktCreateSelfResourceSet(char *name)
00432 {
00433     rk_resource_set_t rs, retval = NULL_RESOURCE_SET;
00434 
00435     require(name != NULL);
00436     require(strlen(name) > 0);
00437     require(strlen(name) < RK_NAME_LEN);
00438     
00439     if( (rs = rk_resource_set_create(name)) != NULL_RESOURCE_SET )
00440     {
00441         struct cpu_reserve_attr cra;
00442         rk_reserve_t cr;
00443         
00444         memset(&cra, 0, sizeof(cra));
00445         /** @todo Come up with some better numbers for the reserve. */
00446         cra.compute_time.tv_sec = 0;
00447         cra.compute_time.tv_nsec = 5000000;
00448         cra.period.tv_sec = 0;
00449         cra.period.tv_nsec = 1000000000;
00450         cra.deadline = cra.period;
00451         cra.blocking_time = cra.start_time = (struct timespec){ 0, 0 };
00452         cra.reserve_type.sch_mode = RSV_SOFT;
00453         cra.reserve_type.enf_mode = RSV_SOFT;
00454         cra.reserve_type.rep_mode = RSV_SOFT;
00455         cra.processor = RK_ANY_CPU;
00456         if( rk_cpu_reserve_create(rs, &cr, &cra) != RK_ERROR )
00457         {
00458             /*
00459              * Note that we do not attach the process here since any children
00460              * we fork(2) off would inherit the resource set.
00461              */
00462             retval = rs;
00463         }
00464         else
00465         {
00466             my_perror("rk_cpu_reserve_create");
00467             rk_resource_set_destroy(rs);
00468         }
00469     }
00470     else
00471     {
00472         my_perror("rk_resource_set_create");
00473     }
00474     return( retval );
00475 }
00476 
00477 /**
00478  * Get a resource set for the child process.  If a resource set already exists
00479  * with the requested name, that will be used.  Otherwise, a new one will be
00480  * created.  If no name is requested, a new set will be created with the
00481  * default name.
00482  *
00483  * @param rs An existing resource set that should be used.
00484  * @param requested_name The requested resource set name or NULL if the default
00485  * should be used.
00486  * @param default_name The default resource set name to use if requested_name
00487  * is NULL.
00488  * @param child_period The child's period in microseconds or zero if no CPU
00489  * reserve should be created.
00490  * @param child_compute The child's compute time in microseconds.
00491  * @return The resource set to use for the child process or NULL_RESOURCE_SET
00492  * if a set could not be created.
00493  */
00494 static rk_resource_set_t rktGetChildResourceSet(rk_resource_set_t rs,
00495                                                 const char *requested_name,
00496                                                 const char *default_name,
00497                                                 unsigned int child_period,
00498                                                 unsigned int child_compute)
00499 {
00500     rk_resource_set_t retval = rs;
00501     
00502     require((requested_name == NULL) ||
00503             ((strlen(requested_name) > 0) &&
00504              (strlen(requested_name) <= RK_NAME_LEN)));
00505     require(default_name != NULL);
00506     require(strlen(default_name) > 0);
00507     require(strlen(default_name) <= RK_NAME_LEN);
00508     require(((child_period == 0) && (child_compute == 0)) ||
00509             (child_compute < child_period));
00510     
00511     /* If no resource set was passed in, try to create one. */
00512     if( retval == NULL_RESOURCE_SET )
00513     {
00514         const char *actual_name;
00515         
00516         if( requested_name != NULL )
00517         {
00518             actual_name = requested_name;
00519         }
00520         else
00521         {
00522             actual_name = default_name;
00523         }
00524         if( (retval = rk_resource_set_create((char *)actual_name)) !=
00525             NULL_RESOURCE_SET )
00526         {
00527             rktimes_data.rkt_Flags |= RKTF_CREATED_RESOURCE_SET;
00528         }
00529         else
00530         {
00531             my_perror("rk_resource_set_create");
00532         }
00533     }
00534     /* Attach the CPU reserve or try to repair a suspicious resource set. */
00535     if( retval != NULL_RESOURCE_SET )
00536     {
00537         rk_reserve_t cr = NULL_RESERVE;
00538         
00539         if( (cr = rk_resource_set_get_cpu_rsv(retval)) != NULL_RESERVE )
00540         {
00541             /*
00542              * Hmm, they already have a reserve, make sure there are some
00543              * processes attached.
00544              */
00545             if( rk_resource_set_get_num_procs(retval) > 0 )
00546             {
00547                 /* Okie, seems fine. */
00548             }
00549             else
00550             {
00551                 /* Suspicious, delete the reserve and make room for our own. */
00552                 if( rk_cpu_reserve_delete(retval) == 0 )
00553                 {
00554                     cr = NULL_RESERVE;
00555                     /* Assume control of the resource set. */
00556                     rktimes_data.rkt_Flags |= RKTF_CREATED_RESOURCE_SET;
00557                 }
00558                 else
00559                 {
00560                     my_perror("rk_cpu_reserve_delete");
00561                 }
00562             }
00563         }
00564         
00565         if( (cr == NULL_RESERVE) && (child_period > 0) )
00566         {
00567             struct cpu_reserve_attr cra;
00568             
00569             cra.compute_time.tv_sec = 0;
00570             cra.compute_time.tv_nsec = child_compute * 1000;
00571             cra.period.tv_sec = 0;
00572             cra.period.tv_nsec = child_period * 1000;
00573             cra.deadline = cra.period;
00574             cra.blocking_time = cra.start_time = (struct timespec){ 0, 0 };
00575             cra.reserve_type.sch_mode = RSV_SOFT;
00576             cra.reserve_type.enf_mode = RSV_SOFT;
00577             cra.reserve_type.rep_mode = RSV_SOFT;
00578             cra.processor = RK_ANY_CPU;
00579             if( rk_cpu_reserve_create(retval, &cr, &cra) < 0 )
00580             {
00581                 my_perror("rk_cpu_reserve_create");
00582             }
00583         }
00584     }
00585     return( retval );
00586 }
00587 
00588 /**
00589  * Print out the usage statement to a file handle.
00590  *
00591  * @param file The destination file handle for the usage output.
00592  * @param prog_name The program name.
00593  */
00594 static void rktUsage(FILE *file, char *prog_name)
00595 {
00596     require(file != NULL);
00597     require(prog_name != NULL);
00598     require(strlen(prog_name) > 0);
00599     
00600     fprintf(file,
00601             "Usage: %s [options] -- <command> [argument ...]\n",
00602             prog_name);
00603     fprintf(file,
00604             "\n"
00605             "Create a resource set and periodically record the CPU\n"
00606             "usage of the attached process and its children.\n"
00607             "\n"
00608             "Options:\n"
00609             "\t-h\t\tThis help message.\n"
00610             "\t-V\t\tShow the version number.\n"
00611             
00612             "\t-o <file>\tBase name for the output files. (Default: rktimes)\n"
00613             
00614             "\t-n <name>\tName of the RK resource set.  If a resource set\n"
00615             "\t\t\twith that name already exists, that will be used.\n"
00616             "\t\t\tOtherwise, a new one with that name will be created.\n"
00617             "\t\t\t(Default: rktimes)\n"
00618             
00619             "\t-p <port>\tServer port for GKrellmd emulation.\n"
00620 
00621             "\t-P <time>\tThe period for the CPU reservation.\n"
00622             "\t\t\tThe -C option must also be given.\n"
00623 
00624             "\t-C <time>\tThe compute time for the CPU reservation.\n"
00625             "\t\t\tThe -P option must also be given.\n"
00626 
00627             "\n"
00628             
00629             "Package: " PACKAGE_STRING "\n"
00630             "Contact: " PACKAGE_BUGREPORT "\n");
00631 }
00632 
00633 /**
00634  * Process the command line options.
00635  *
00636  * @param argc_inout Pointer to main's argc variable.  On return, the variable
00637  * will contain the number of arguments remaining after option processing.
00638  * @param argv_inout Pointer to main's argv variable.  On return, the variable
00639  * will contain the remaining argument values.
00640  * @return Zero if the options were processed correctly, > 0 if there was an
00641  * error and the usage should be printed, or < 0 if the program should exit
00642  * immediately.
00643  */
00644 static int rktProcessOptions(int *argc_inout, char **argv_inout[])
00645 {
00646     unsigned long long us_time;
00647     int ch, retval = 0;
00648     char *prog_name;
00649     char **argv;
00650     int argc;
00651 
00652     require(argc_inout != NULL);
00653     require(argv_inout != NULL);
00654 
00655     argc = *argc_inout;
00656     argv = *argv_inout;
00657     prog_name = argv[0];
00658     while( ((ch = getopt(argc, argv, "hVo:n:p:P:C:")) != -1) && (retval == 0) )
00659     {
00660         switch( ch )
00661         {
00662         case 'o':
00663             /* Output file base name. */
00664             if( strlen(optarg) == 0 )
00665             {
00666                 fprintf(stderr,
00667                         "%s: -o value is empty\n",
00668                         prog_name);
00669                 retval = 1;
00670             }
00671             else
00672             {
00673                 rktimes_data.rkt_OutputBase = optarg;
00674             }
00675             break;
00676         case 'n':
00677             /* Resource set name. */
00678             if( strlen(optarg) == 0 )
00679             {
00680                 fprintf(stderr,
00681                         "%s: -n value is empty\n",
00682                         prog_name);
00683                 retval = 1;
00684             }
00685             else if( strlen(optarg) > RK_NAME_LEN )
00686             {
00687                 fprintf(stderr,
00688                         "%s: -n value too long (only %d characters allowed)\n",
00689                         prog_name,
00690                         RK_NAME_LEN);
00691                 retval = 1;
00692             }
00693             else
00694             {
00695                 rktimes_data.rkt_Name = optarg;
00696             }
00697             break;
00698         case 'P':
00699             /* Period for the CPU reservation. */
00700             if( string_to_microsec(&us_time, optarg) )
00701             {
00702                 rktimes_data.rkt_ChildPeriod = us_time;
00703             }
00704             else
00705             {
00706                 fprintf(stderr,
00707                         "%s: -P option requires a time value\n",
00708                         prog_name);
00709                 retval = 1;
00710             }
00711             break;
00712         case 'C':
00713             /* Compute time for the CPU reservation. */
00714             if( string_to_microsec(&us_time, optarg) )
00715             {
00716                 rktimes_data.rkt_ChildCompute = us_time;
00717             }
00718             else
00719             {
00720                 fprintf(stderr,
00721                         "%s: -C option requires a time value\n",
00722                         prog_name);
00723                 retval = 1;
00724             }
00725             break;
00726         case 'p':
00727             /* Server port number for GKrellm emulation. */
00728             {
00729                 int port;
00730                 
00731                 if( (sscanf(optarg, "%d", &port) == 1) &&
00732                     (port > 0) && (port < 65536) )
00733                 {
00734                     int fd;
00735                     
00736                     if( (fd = socket(PF_INET, SOCK_STREAM, 0)) == -1 )
00737                     {
00738                         my_perror("socket");
00739                         retval = -1;
00740                     }
00741                     else
00742                     {
00743                         struct sockaddr_in sin;
00744                         int on = 1;
00745                         
00746                         (void)setsockopt(fd,
00747                                          SOL_SOCKET,
00748                                          SO_REUSEADDR,
00749                                          (char *)&on,
00750                                          sizeof(on));
00751 #if defined(BSD44)
00752                         sin.sin_len = sizeof(sin);
00753 #endif
00754                         sin.sin_family = AF_INET;
00755                         sin.sin_port = htons(port);
00756                         sin.sin_addr.s_addr = INADDR_ANY;
00757                         if( bind(fd,
00758                                  (struct sockaddr *)&sin,
00759                                  sizeof(sin)) == -1 )
00760                         {
00761                             my_perror("bind");
00762                             retval = -1;
00763                         }
00764                         else if( listen(fd, 5) == -1 )
00765                         {
00766                             my_perror("listen");
00767                             retval = -1;
00768                         }
00769                         else
00770                         {
00771                             int fl;
00772                             
00773                             fl = fcntl(fd, F_GETFL, 0);
00774                             fl |= O_NONBLOCK |
00775 #if defined(O_ASYNC)
00776                                 O_ASYNC
00777 #elif defined(FASYNC)
00778                                 FASYNC
00779 #endif
00780                                 ;
00781                             fcntl(fd, F_SETFL, fl);
00782                             fcntl(fd, F_SETOWN, getpid());
00783                             /*
00784                              * Note: SIGIO is ignored by default, so we do not
00785                              * have to worry about handling it yet.
00786                              */
00787                         }
00788                     }
00789                     if( retval == 0 )
00790                     {
00791                         rktimes_data.rkt_ServerSocket = fd;
00792                     }
00793                     else
00794                     {
00795                         close(fd);
00796                     }
00797                 }
00798                 else
00799                 {
00800                     fprintf(stderr,
00801                             "%s: Invalid -p value: %s\n",
00802                             prog_name,
00803                             optarg);
00804                     retval = 1;
00805                 }
00806             }
00807             break;
00808         case 'V':
00809             fprintf(stderr, "%s\n", PACKAGE_VERSION);
00810             retval = -1;
00811             break;
00812         case 'h':
00813         case '?':
00814         default:
00815             retval = 1;
00816             break;
00817         }
00818     }
00819     /* Make sure any compute and period times are sane. */
00820     if( (rktimes_data.rkt_ChildCompute == 0) &&
00821         (rktimes_data.rkt_ChildPeriod == 0) )
00822     {
00823     }
00824     else if( rktimes_data.rkt_ChildCompute == 0 )
00825     {
00826         fprintf(stderr,
00827                 "%s: Compute time _must_ be specified with period\n",
00828                 prog_name);
00829         retval = 1;
00830     }
00831     else if( rktimes_data.rkt_ChildPeriod == 0 )
00832     {
00833         fprintf(stderr,
00834                 "%s: Period time _must_ be specified with compute time\n",
00835                 prog_name);
00836         retval = 1;
00837     }
00838     else if( rktimes_data.rkt_ChildCompute >= rktimes_data.rkt_ChildPeriod )
00839     {
00840         fprintf(stderr,
00841                 "%s: Compute time _must_ be less than the period\n",
00842                 prog_name);
00843         retval = 1;
00844     }
00845     *argc_inout -= optind;
00846     *argv_inout += optind;
00847     /* Optionally skip the '--' that is used to terminate the option list. */
00848     if( (*argc_inout > 0) && (strcmp(*argv_inout[0], "--") == 0) )
00849     {
00850         *argc_inout -= 1;
00851         *argv_inout += 1;
00852     }
00853     if( (retval == 0) && (*argc_inout == 0) )
00854     {
00855         /* No arguments were given, make sure there is a name and */
00856         if( rktimes_data.rkt_Name == NULL )
00857         {
00858             fprintf(stderr, "%s: Missing utility to monitor\n", prog_name);
00859             retval = 1;
00860         }
00861         /* ... it is a valid resource set. */
00862         else if( (rktimes_data.rkt_ResourceSet =
00863                   rk_resource_set_get_by_name(rktimes_data.rkt_Name)) ==
00864                  NULL_RESOURCE_SET )
00865         {
00866             fprintf(stderr,
00867                     "%s: No such resource set: %s\n",
00868                     prog_name,
00869                     rktimes_data.rkt_Name);
00870             retval = 1;
00871         }
00872     }
00873     return( retval );
00874 }
00875 
00876 /**
00877  * The parent portion of the fork(2) between rktimes and the monitored utility.
00878  * This function will attach itself to a resource set to ensure that it has
00879  * some CPU time to work and then wait for gkrellm connections or the child's
00880  * death.
00881  *
00882  * @param rs The resource set for the rktimes process.
00883  * @return The return code for main().
00884  *
00885  * @sa rktChildPart
00886  */
00887 static int rktParentPart(rk_resource_set_t rs)
00888 {
00889     int retval = EXIT_FAILURE;
00890     struct itimerval itv;
00891     struct sigaction sa;
00892     sigset_t sigmask;
00893     
00894     require(rs != NULL_RESOURCE_SET);
00895 
00896     if( rktimes_data.rkt_ChildPID != 0 )
00897     {
00898         char scratch[PATH_MAX];
00899         FILE *pid_file;
00900         
00901         snprintf(scratch,
00902                  sizeof(scratch),
00903                  "%s-child.pid",
00904                  rktimes_data.rkt_OutputBase);
00905         if( (pid_file = fopen(scratch, "w")) != NULL )
00906         {
00907             fprintf(pid_file, "%d", rktimes_data.rkt_ChildPID);
00908             fclose(pid_file);
00909             pid_file = NULL;
00910         }
00911     }
00912 
00913     /* Attach ourselves here to avoid inheritance problem. */
00914     if( rk_resource_set_attach_process(rs, getpid()) < 0 )
00915     {
00916         my_perror("rk_resource_set_attach_process");
00917     }
00918 
00919     /*
00920      * We use signals as a primitive event system.  So any that we use, and are
00921      * not passed on to the child, need to be blocked before setting up the
00922      * handlers.  Once everything has been setup, we will use sigsuspend(2) to
00923      * atomically unblock and wait for the signals to arrive.
00924      */
00925     sigemptyset(&sigmask);
00926     sigaddset(&sigmask, SIGALRM);
00927     sigaddset(&sigmask, SIGCHLD);
00928     sigaddset(&sigmask, SIGIO);
00929     sigaddset(&sigmask, SIGINT);
00930     sigaddset(&sigmask, SIGTERM);
00931     if( sigprocmask(SIG_BLOCK, &sigmask, NULL) < 0 )
00932     {
00933         my_perror("sigprocmask");
00934         
00935         ensure(0);
00936     }
00937 
00938     /* Setup the signal handlers. */
00939     sa.sa_mask = sigmask;
00940     sa.sa_flags = 0;
00941 #if defined(SA_RESTART)
00942     sa.sa_flags |= SA_RESTART;
00943 #endif
00944 
00945     signal(SIGPIPE, SIG_IGN);
00946 
00947     sa.sa_handler = sigalrm;
00948     sigaction(SIGALRM, &sa, NULL);
00949 
00950     if( rktimes_data.rkt_ServerSocket != -1 )
00951     {
00952         sa.sa_handler = sigio;
00953         sigaction(SIGIO, &sa, NULL);
00954     }
00955     
00956     if( rktimes_data.rkt_ChildPID == 0 )
00957     {
00958         /*
00959          * No utility is being monitored, just a resource set.  Therefore we
00960          * need to exit when these signals are received since there is no
00961          * child to pass them to.
00962          */
00963         sa.sa_handler = sigexit;
00964         sigaction(SIGINT, &sa, NULL);
00965         sigaction(SIGTERM, &sa, NULL);
00966     }
00967     else
00968     {
00969         /* Catch SIGCHLD so we know when the child exited. */
00970         sa.sa_handler = sigchld;
00971         sigaction(SIGCHLD, &sa, NULL);
00972 
00973         /*
00974          * Pass SIGINT/SIGTERM onto the child so they can handle them as they
00975          * choose.  If they exit, we will get the SIGCHLD and exit shortly
00976          * thereafter.
00977          */
00978         sa.sa_handler = sigpass;
00979         sigaction(SIGINT, &sa, NULL);
00980         sigaction(SIGTERM, &sa, NULL);
00981     }
00982     
00983     itv.it_interval.tv_sec = 0;
00984     itv.it_interval.tv_usec = 1000000 / SAMPLES_PER_SECOND;
00985     itv.it_value.tv_sec = 0;
00986     itv.it_value.tv_usec = 1000000 / SAMPLES_PER_SECOND;
00987     /** @todo Use rk periodic stuff here. */
00988     if( setitimer(ITIMER_REAL, &itv, NULL) == 0 )
00989     {
00990         sigset_t empty_sigmask;
00991         int status;
00992 
00993         sigemptyset(&empty_sigmask);
00994         
00995         /* Keep handling signals until the child has died. */
00996         while( !(rktimes_data.rkt_Flags & RKTF_DONE) )
00997         {
00998             sigsuspend(&empty_sigmask);
00999         }
01000 
01001         /* Cleanup the child's status. */
01002         if( rktimes_data.rkt_ChildPID == 0 )
01003         {
01004             /*
01005              * No child was being monitored, ergo, there is nothing to wait for
01006              */
01007         }
01008         else if( wait(&status) >= 0 )
01009         {
01010             if( WIFEXITED(status) )
01011             {
01012                 retval = WEXITSTATUS(status);
01013             }
01014             else if( WIFSIGNALED(status) )
01015             {
01016                 retval = EXIT_SUCCESS;
01017             }
01018             else
01019             {
01020                 retval = EXIT_FAILURE;
01021             }
01022         }
01023         else
01024         {
01025             my_perror("wait");
01026             retval = EXIT_FAILURE;
01027         }
01028 
01029         /* Clear the timer. */
01030         memset(&itv, 0, sizeof(itv));
01031         if( setitimer(ITIMER_REAL, &itv, NULL) < 0 )
01032         {
01033             my_perror("setitimer");
01034 
01035             ensure(0);
01036         }
01037     }
01038     else
01039     {
01040         my_perror("setitimer");
01041         retval = EXIT_FAILURE;
01042     }
01043 
01044     /* We will be dying soon, ignore any signals and */
01045     signal(SIGALRM, SIG_IGN);
01046     signal(SIGCHLD, SIG_IGN);
01047     signal(SIGINT, SIG_IGN);
01048     signal(SIGTERM, SIG_IGN);
01049     signal(SIGIO, SIG_IGN);
01050 
01051     /* ... restore the old signal mask. */
01052     if( sigprocmask(SIG_UNBLOCK, &sigmask, NULL) < 0 )
01053     {
01054         my_perror("sigprocmask");
01055 
01056         ensure(0);
01057     }
01058     
01059     return( retval );
01060 }
01061 
01062 /**
01063  * The child portion of the fork(2) between rktimes and the monitored utility.
01064  * This function will attach the child to the given resource set and execvp(3)
01065  * the utility to monitor with the given arguments.
01066  *
01067  * @param rs The child's resource set.
01068  * @param argv The utility to startup and its arguments.
01069  * @return A failure exit code, otherwise this function will not return because
01070  * of the execvp(3).
01071  *
01072  * @sa rktParentPart
01073  */
01074 static int rktChildPart(rk_resource_set_t rs, char *argv[])
01075 {
01076     int rc, retval = EXIT_SUCCESS;
01077     sigset_t sigmask;
01078     int lpc;
01079     
01080     require(rs != NULL_RESOURCE_SET);
01081     require(argv != NULL);
01082     
01083     /*
01084      * Make sure any children created by the monitored utility are also
01085      * attached to the resource set.
01086      */
01087     rk_inherit_mode(1);
01088     if( rk_resource_set_attach_process(rs, getpid()) == 0 )
01089     {
01090     }
01091     else
01092     {
01093         my_perror("rk_resource_set_attach_process");
01094         retval = EXIT_FAILURE;
01095     }
01096 
01097     /*
01098      * Cleanup the file descriptors.
01099      *
01100      * XXX Should we do this?  Or, should rktimes be completely invisible and
01101      * pass everything through.
01102      */
01103 
01104     /* First, close our server socket, */
01105     if( rktimes_data.rkt_ServerSocket != -1 )
01106     {
01107         close(rktimes_data.rkt_ServerSocket);
01108         rktimes_data.rkt_ServerSocket = -1;
01109     }
01110 
01111     /* ... then make sure stdio is sane, and */
01112     do {
01113         if( (rc = open("/dev/null", O_RDWR)) == -1 )
01114         {
01115             fprintf(stderr, "Error: Cannot open '/dev/null'?");
01116             retval = EXIT_FAILURE;
01117         }
01118         else if( rc > 2 )
01119         {
01120             close(rc); /* not needed. */
01121         }
01122         else
01123         {
01124             /* stdin/stderr/stdout filled. */
01125         }
01126     } while( (rc >= 0) && (rc <= 2) );
01127 
01128     /* ... make sure we do not leak any descriptors. */
01129     for( lpc = 3; lpc < FD_SETSIZE; lpc++ )
01130     {
01131         if( close(lpc) != -1 )
01132         {
01133             fprintf(stderr,
01134                     "Warning: Descriptor %d was leaked from rktimes.\n",
01135                     lpc);
01136         }
01137     }
01138     
01139     /* Reset the signal mask for the child. */
01140     sigfillset(&sigmask);
01141     if( sigprocmask(SIG_UNBLOCK, &sigmask, NULL) < 0 )
01142     {
01143         my_perror("sigprocmask");
01144         
01145         ensure(0);
01146     }
01147 
01148     if( retval == EXIT_SUCCESS )
01149     {
01150         execvp(argv[0], argv);
01151 
01152         /* FALLTHROUGH, normal operation will not reach this point. */
01153         perror(argv[0]);
01154         switch( errno )
01155         {
01156         case ENOENT:
01157         case EPERM:
01158             retval = 127;
01159             break;
01160         default:
01161             retval = EXIT_FAILURE;
01162             break;
01163         }
01164     }
01165     return( retval );
01166 }
01167 
01168 int main(int argc, char *argv[])
01169 {
01170     int lpc, retval = EXIT_FAILURE;
01171 
01172     /* Default values. */
01173     rktimes_data.rkt_OutputBase = "rktimes";
01174     rktimes_data.rkt_ServerSocket = -1;
01175     for( lpc = 0; lpc < MAX_CLIENTS; lpc++ )
01176     {
01177         rktimes_data.rkt_ClientSockets[lpc] = -1;
01178     }
01179 
01180     /* Initialize the internal bits first. */
01181     if( cpInitChildProcessData() )
01182     {
01183         char *prog_name = argv[0];
01184         int rc;
01185 
01186         rc = rktProcessOptions(&argc, &argv);
01187         if( (rc == 0) && ((argc > 0) || (rktimes_data.rkt_Name != NULL)) )
01188         {
01189             char self_rs_name[RK_NAME_LEN + 1];
01190             rk_resource_set_t rs;
01191 
01192             if( argc > 0 )
01193             {
01194                 rktimes_data.rkt_UtilityName = argv[0];
01195             }
01196             else
01197             {
01198                 rktimes_data.rkt_UtilityName = "(none)";
01199             }
01200             snprintf(self_rs_name, RK_NAME_LEN + 1, "rktimes.%d", getpid());
01201 
01202             /*
01203              * Block signals so we do not die without cleaning up the resource
01204              * set(s).
01205              */
01206             {
01207                 sigset_t sigmask;
01208                 
01209                 sigaddset(&sigmask, SIGINT);
01210                 sigaddset(&sigmask, SIGTERM);
01211                 if( sigprocmask(SIG_BLOCK, &sigmask, NULL) < 0 )
01212                 {
01213                     my_perror("sigprocmask");
01214                     
01215                     ensure(0);
01216                 }
01217             }
01218             
01219             rs = rktCreateSelfResourceSet(self_rs_name);
01220             rktimes_data.rkt_ResourceSet =
01221                 rktGetChildResourceSet(rktimes_data.rkt_ResourceSet,
01222                                        rktimes_data.rkt_Name,
01223                                        "rktimes",
01224                                        rktimes_data.rkt_ChildPeriod,
01225                                        rktimes_data.rkt_ChildCompute);
01226             if( (rs != NULL_RESOURCE_SET) &&
01227                 (rktimes_data.rkt_ResourceSet != NULL_RESOURCE_SET) )
01228             {
01229                 gettimeofday(&rktimes_data.rkt_StartTime, NULL);
01230                 if( argc == 0 )
01231                 {
01232                     retval = rktParentPart(rs);
01233                 }
01234                 else if( (rktimes_data.rkt_ChildPID = fork()) > 0 )
01235                 {
01236                     retval = rktParentPart(rs);
01237                 }
01238                 else if( rktimes_data.rkt_ChildPID == 0 )
01239                 {
01240                     return( rktChildPart(rktimes_data.rkt_ResourceSet, argv) );
01241                 }
01242                 else
01243                 {
01244                     my_perror("fork");
01245                     retval = EXIT_FAILURE;
01246                 }
01247             }
01248             if( rktimes_data.rkt_Flags & RKTF_CREATED_RESOURCE_SET )
01249             {
01250                 rk_resource_set_destroy(rktimes_data.rkt_ResourceSet);
01251                 rktimes_data.rkt_ResourceSet = NULL_RESOURCE_SET;
01252             }
01253             rk_resource_set_destroy(rs);
01254         }
01255         else if( rc >= 0 )
01256         {
01257             rktUsage(stderr, prog_name);
01258         }
01259         if( rktimes_data.rkt_ServerSocket != -1 )
01260         {
01261             close(rktimes_data.rkt_ServerSocket);
01262             rktimes_data.rkt_ServerSocket = -1;
01263         }
01264         cpKillChildProcessData();
01265     }
01266     
01267     return( retval );
01268 }

Generated on Fri Oct 22 07:50:24 2004 for CPU Broker by  doxygen 1.3.9.1