source: trunk/lang/tcl/src/RpRlimit.c @ 1018

Last change on this file since 1018 was 1018, checked in by gah, 16 years ago

Massive changes: New directory/file layout

File size: 10.4 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  Rappture::rlimit
4 *
5 *  This is an interface to the system getrlimit() and setrlimit()
6 *  routines.  It allows you to get/set resource limits for child
7 *  executables.  We use this in Rappture::exec, for example, to make
8 *  sure that jobs don't run forever or fill up the disk.
9 * ======================================================================
10 *  AUTHOR:  Michael McLennan, Purdue University
11 *  Copyright (c) 2004-2006  Purdue Research Foundation
12 *
13 *  See the file "license.terms" for information on usage and
14 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 * ======================================================================
16 */
17#include <tcl.h>
18#include <string.h>
19#ifndef _WIN32
20#include <sys/time.h>
21#include <sys/resource.h>
22#else
23#include "RpWinResource.h"
24#endif
25
26#include <errno.h>
27#include "RpOp.h"
28
29static Tcl_ObjCmdProc RpRlimitCmd;
30static Tcl_ObjCmdProc RpRlimitGetOp;
31static Tcl_ObjCmdProc RpRlimitSetOp;
32static int RpRlimitOptionError(Tcl_Interp *interp, const char *string);
33
34/*
35 * rlimit subcommands:
36 */
37static Rp_OpSpec rlimitOps[] = {
38    {"get", 1, RpRlimitGetOp, 2, 4, "?-soft|-hard? ?option?",},
39    {"set", 1, RpRlimitSetOp, 2, 0, "?option value ...?",},
40};
41static int nRlimitOps = sizeof(rlimitOps) / sizeof(Rp_OpSpec);
42
43/*
44 * rlimit configuration options:
45 */
46typedef struct RpRlimitNames {
47    char *name;
48    int resid;
49} RpRlimitNames;
50
51static RpRlimitNames rlimitOptions[] = {
52    {"coresize",  RLIMIT_CORE},
53    {"cputime",   RLIMIT_CPU},
54    {"filesize",  RLIMIT_FSIZE},
55    {(char*)NULL, 0}
56};
57
58/*
59 * ------------------------------------------------------------------------
60 *  RpRlimit_Init()
61 *
62 *  Called in Rappture_Init() to initialize the commands defined
63 *  in this file.
64 * ------------------------------------------------------------------------
65 */
66int
67RpRlimit_Init(interp)
68    Tcl_Interp *interp;  /* interpreter being initialized */
69{
70    Tcl_CreateObjCommand(interp, "::Rappture::rlimit", RpRlimitCmd, NULL, NULL);
71    return TCL_OK;
72}
73
74/*
75 * ------------------------------------------------------------------------
76 *  RpRlimitCmd()
77 *
78 *  Invoked whenever someone uses the "rlimit" command to get/set
79 *  limits for child processes.  Handles the following syntax:
80 *
81 *      rlimit get ?-soft|-hard? ?<option>?
82 *      rlimit set ?<option> <value> <option> <value> ...?
83 *
84 *  Returns TCL_OK on success, and TCL_ERROR (along with an error
85 *  message in the interpreter) if anything goes wrong.
86 * ------------------------------------------------------------------------
87 */
88static int
89RpRlimitCmd(cdata, interp, objc, objv)
90    ClientData cdata;         /* not used */
91    Tcl_Interp *interp;       /* interpreter handling this request */
92    int objc;                 /* number of command line args */
93    Tcl_Obj *const *objv;     /* strings for command line args */
94{
95    Tcl_ObjCmdProc *proc;
96
97    proc = Rp_GetOpFromObj(interp, nRlimitOps, rlimitOps, RP_OP_ARG1,
98        objc, objv, 0);
99    if (proc == NULL) {
100        return TCL_ERROR;
101    }
102    return (*proc)(cdata, interp, objc, objv);
103}
104
105/*
106 * ------------------------------------------------------------------------
107 *  RpRlimitGetOp()
108 *
109 *  Invoked whenever someone uses the "rlimit get" command to query
110 *  limits for child processes.  Handles the following syntax:
111 *
112 *      rlimit get ?-soft|-hard? ?<option>?
113 *
114 *  Returns TCL_OK on success, and TCL_ERROR (along with an error
115 *  message in the interpreter) if anything goes wrong.
116 * ------------------------------------------------------------------------
117 */
118static int
119RpRlimitGetOp(cdata, interp, objc, objv)
120    ClientData cdata;         /* not used */
121    Tcl_Interp *interp;       /* interpreter handling this request */
122    int objc;                 /* number of command line args */
123    Tcl_Obj *const *objv;     /* strings for command line args */
124{
125    int hardlim = 0;  /* default: use -soft limit */
126    int nextarg = 2;  /* start with this arg */
127    Tcl_Obj *objPtr;
128    int i, status;
129    struct rlimit rvals;
130    rlim_t *rvalptr;
131 
132    if (nextarg < objc) {
133        const char *string;
134
135        string = Tcl_GetString(objv[nextarg]);
136        if (string[0] == '-') {
137            if (strcmp(string,"-soft") == 0) {
138                hardlim = 0;
139                nextarg++;
140            } else if (strcmp(string,"-hard") == 0) {
141                hardlim = 1;
142                nextarg++;
143            } else {
144                Tcl_AppendResult(interp, "bad option \"", string,
145                "\": should be -soft or -hard", (char*)NULL);
146                return TCL_ERROR;
147            }
148        }
149    }
150
151    /*
152     * No args?  Then return limits for all options.
153     */
154    if (nextarg >= objc) {
155        Tcl_Obj *listObjPtr;
156
157        listObjPtr = Tcl_NewListObj(0, NULL);
158        for (i=0; rlimitOptions[i].name != NULL; i++) {
159            Tcl_Obj *objPtr;
160            status = getrlimit(rlimitOptions[i].resid, &rvals);
161            if (status != 0) {
162                Tcl_ResetResult(interp);
163                Tcl_AppendResult(interp, "unexpected rlimit failure",
164                    (char*)NULL);
165                return TCL_ERROR;
166            }
167            if (hardlim) {
168                rvalptr = &rvals.rlim_max;
169            } else {
170                rvalptr = &rvals.rlim_cur;
171            }
172            objPtr = Tcl_NewStringObj(rlimitOptions[i].name, -1);
173            Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
174            if (*rvalptr == RLIM_INFINITY) {
175                objPtr = Tcl_NewStringObj("unlimited", -1);
176            } else {
177                objPtr = Tcl_NewLongObj((unsigned long)*rvalptr);
178            }
179            Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
180        }
181        Tcl_SetObjResult(interp, listObjPtr);
182        return TCL_OK;
183    }
184
185    /*
186     * Find the limit for the specified option.
187     */
188    for (i=0; rlimitOptions[i].name != NULL; i++) {
189        const char *string;
190
191        string = Tcl_GetString(objv[nextarg]);
192        if (strcmp(string, rlimitOptions[i].name) == 0) {
193            break;
194        }
195    }
196    if (rlimitOptions[i].name == NULL) {
197        const char *string;
198
199        string = Tcl_GetString(objv[nextarg]);
200        return RpRlimitOptionError(interp, string);
201    }
202
203    status = getrlimit(rlimitOptions[i].resid, &rvals);
204    if (status != 0) {
205        Tcl_ResetResult(interp);
206        Tcl_AppendResult(interp, "unexpected rlimit failure",
207            (char*)NULL);
208        return TCL_ERROR;
209    }
210    if (hardlim) {
211        rvalptr = &rvals.rlim_max;
212    } else {
213        rvalptr = &rvals.rlim_cur;
214    }
215
216    if (*rvalptr == RLIM_INFINITY) {
217        objPtr = Tcl_NewStringObj("unlimited", -1);
218    } else {
219        objPtr = Tcl_NewLongObj((unsigned long)*rvalptr);
220    }
221    Tcl_SetObjResult(interp, objPtr);
222    return TCL_OK;
223}
224
225/*
226 * ------------------------------------------------------------------------
227 *  RpRlimitSetOp()
228 *
229 *  Invoked whenever someone uses the "rlimit set" command to query
230 *  limits for child processes.  Handles the following syntax:
231 *
232 *      rlimit set ?<option> <value> <option> <value> ...?
233 *
234 *  Returns TCL_OK on success, and TCL_ERROR (along with an error
235 *  message in the interpreter) if anything goes wrong.
236 * ------------------------------------------------------------------------
237 */
238static int
239RpRlimitSetOp(cdata, interp, objc, objv)
240    ClientData cdata;         /* not used */
241    Tcl_Interp *interp;       /* interpreter handling this request */
242    int objc;                 /* number of command line args */
243    Tcl_Obj *const *objv;   /* strings for command line args */
244{
245    int i, n, lim, status;
246    struct rlimit rvals;
247    rlim_t newval;
248
249    for (n=2; n < objc; n += 2) {
250        const char *name, *value;
251
252        name = Tcl_GetString(objv[n]);
253        if (n+1 >= objc) {
254            Tcl_AppendResult(interp, "missing value for option \"",
255                name, "\"", (char*)NULL);
256            return TCL_ERROR;
257        }
258        value = Tcl_GetString(objv[n+1]);
259        if (strcmp(value, "unlimited") == 0) {
260            newval = RLIM_INFINITY;
261        } else if (Tcl_GetIntFromObj(interp, objv[n+1], &lim) == TCL_OK) {
262            if (lim < 0) {
263                Tcl_ResetResult(interp);
264                Tcl_AppendResult(interp, "bad value \"", value,
265                    "\": should be integer >= 0 or \"unlimited\"",
266                    (char*)NULL);
267                return TCL_ERROR;
268            }
269            newval = (rlim_t)lim;
270        } else {
271            Tcl_ResetResult(interp);
272            Tcl_AppendResult(interp, "bad value \"", value,
273                "\": should be integer >= 0 or \"unlimited\"", (char*)NULL);
274            return TCL_ERROR;
275        }
276
277        /*
278         * Find the option being changed and set it.
279         */
280        for (i=0; rlimitOptions[i].name != NULL; i++) {
281            if (strcmp(name, rlimitOptions[i].name) == 0) {
282                break;
283            }
284        }
285        if (rlimitOptions[i].name == NULL) {
286            return RpRlimitOptionError(interp, name);
287        }
288        status = getrlimit(rlimitOptions[i].resid, &rvals);
289        if (status == 0) {
290            rvals.rlim_cur = newval;
291            status = setrlimit(rlimitOptions[i].resid, &rvals);
292        }
293        if (status == EPERM) {
294            Tcl_ResetResult(interp);
295            Tcl_AppendResult(interp, "value \"", value,
296                "\" set too high for option \"", name,
297                (char*)NULL);
298            return TCL_ERROR;
299        } else if (status != 0) {
300            Tcl_ResetResult(interp);
301            Tcl_AppendResult(interp, "unexpected rlimit failure",
302                (char*)NULL);
303            return TCL_ERROR;
304        }
305    }
306    return TCL_OK;
307}
308
309/*
310 * ------------------------------------------------------------------------
311 *  RpRlimitOptionError()
312 *
313 *  Used internally to set an error message in the interpreter
314 *  describing an unrecognized rlimit option.  Sets the result of
315 *  the Tcl interpreter and returns an error status code.
316 * ------------------------------------------------------------------------
317 */
318static int
319RpRlimitOptionError(interp, option)
320    Tcl_Interp *interp;       /* interpreter handling this request */
321    const char *option;       /* bad option supplied by the user */
322{
323    int i;
324
325    Tcl_AppendResult(interp, "bad option \"", option,
326        "\": should be one of ",(char*)NULL);
327
328    for (i=0; rlimitOptions[i].name != NULL; i++) {
329        if (i > 0) {
330            Tcl_AppendResult(interp, ", ", (char*)NULL);
331        }
332        Tcl_AppendResult(interp, rlimitOptions[i].name, (char*)NULL);
333    }
334    return TCL_ERROR;
335}
Note: See TracBrowser for help on using the repository browser.