* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <errno.h>
#include <unistd.h>
#include <sys/resource.h>
+#include <libsmartcols.h>
+
#include "c.h"
#include "nls.h"
-#include "tt.h"
#include "xalloc.h"
#include "strutils.h"
+#include "list.h"
+#include "closestream.h"
#ifndef RLIMIT_RTTIME
# define RLIMIT_RTTIME 15
STACK
};
+/* basic output flags */
+static int no_headings;
+static int raw;
+
struct prlimit_desc {
const char *name;
const char *help;
static struct prlimit_desc prlimit_desc[] =
{
[AS] = { "AS", N_("address space limit"), N_("bytes"), RLIMIT_AS },
- [CORE] = { "CORE", N_("max core file size"), N_("blocks"), RLIMIT_CORE },
+ [CORE] = { "CORE", N_("max core file size"), N_("bytes"), RLIMIT_CORE },
[CPU] = { "CPU", N_("CPU time"), N_("seconds"), RLIMIT_CPU },
[DATA] = { "DATA", N_("max data size"), N_("bytes"), RLIMIT_DATA },
- [FSIZE] = { "FSIZE", N_("max file size"), N_("blocks"), RLIMIT_FSIZE },
- [LOCKS] = { "LOCKS", N_("max amount of file locks held"), NULL, RLIMIT_LOCKS },
+ [FSIZE] = { "FSIZE", N_("max file size"), N_("bytes"), RLIMIT_FSIZE },
+ [LOCKS] = { "LOCKS", N_("max number of file locks held"), N_("locks"), RLIMIT_LOCKS },
[MEMLOCK] = { "MEMLOCK", N_("max locked-in-memory address space"), N_("bytes"), RLIMIT_MEMLOCK },
[MSGQUEUE] = { "MSGQUEUE", N_("max bytes in POSIX mqueues"), N_("bytes"), RLIMIT_MSGQUEUE },
[NICE] = { "NICE", N_("max nice prio allowed to raise"), NULL, RLIMIT_NICE },
- [NOFILE] = { "NOFILE", N_("max amount of open files"), NULL, RLIMIT_NOFILE },
- [NPROC] = { "NPROC", N_("max number of processes"), NULL, RLIMIT_NPROC },
- [RSS] = { "RSS", N_("max resident set size"), N_("pages"), RLIMIT_RSS },
+ [NOFILE] = { "NOFILE", N_("max number of open files"), N_("files"), RLIMIT_NOFILE },
+ [NPROC] = { "NPROC", N_("max number of processes"), N_("processes"), RLIMIT_NPROC },
+ [RSS] = { "RSS", N_("max resident set size"), N_("bytes"), RLIMIT_RSS },
[RTPRIO] = { "RTPRIO", N_("max real-time priority"), NULL, RLIMIT_RTPRIO },
[RTTIME] = { "RTTIME", N_("timeout for real-time tasks"), N_("microsecs"), RLIMIT_RTTIME },
- [SIGPENDING] = { "SIGPENDING", N_("max amount of pending signals"), NULL, RLIMIT_SIGPENDING },
+ [SIGPENDING] = { "SIGPENDING", N_("max number of pending signals"), N_("signals"), RLIMIT_SIGPENDING },
[STACK] = { "STACK", N_("max stack size"), N_("bytes"), RLIMIT_STACK }
};
+#define MAX_RESOURCES ARRAY_SIZE(prlimit_desc)
+
struct prlimit {
+ struct list_head lims;
+
struct rlimit rlim;
struct prlimit_desc *desc;
- int modify;
+ int modify; /* PRLIMIT_{SOFT,HARD} mask */
};
#define PRLIMIT_EMPTY_LIMIT {{ 0, 0, }, NULL, 0 }
struct colinfo {
const char *name; /* header */
double whint; /* width hint (N < 1 is in percent of termwidth) */
- int flags; /* TT_FL_* */
+ int flags; /* SCOLS_FL_* */
const char *help;
};
/* columns descriptions */
-struct colinfo infos[] = {
- [COL_RES] = { "RESOURCE", 0.25, TT_FL_TRUNC, N_("resource name") },
- [COL_HELP] = { "DESCRIPTION", 0.1, TT_FL_TRUNC, N_("resource description")},
- [COL_SOFT] = { "SOFT", 0.1, TT_FL_RIGHT, N_("soft limit")},
- [COL_HARD] = { "HARD", 1, TT_FL_RIGHT, N_("hard limit (ceiling)")},
- [COL_UNITS] = { "UNITS", 0.1, TT_FL_TRUNC, N_("units")},
+static struct colinfo infos[] = {
+ [COL_RES] = { "RESOURCE", 0.25, SCOLS_FL_TRUNC, N_("resource name") },
+ [COL_HELP] = { "DESCRIPTION", 0.1, SCOLS_FL_TRUNC, N_("resource description")},
+ [COL_SOFT] = { "SOFT", 0.1, SCOLS_FL_RIGHT, N_("soft limit")},
+ [COL_HARD] = { "HARD", 1, SCOLS_FL_RIGHT, N_("hard limit (ceiling)")},
+ [COL_UNITS] = { "UNITS", 0.1, SCOLS_FL_TRUNC, N_("units")},
};
-#define NCOLS ARRAY_SIZE(infos)
-#define MAX_RESOURCES ARRAY_SIZE(prlimit_desc)
+static int columns[ARRAY_SIZE(infos) * 2];
+static int ncolumns;
+
+
#define INFINITY_STR "unlimited"
#define INFINITY_STRLEN (sizeof(INFINITY_STR) - 1)
#define PRLIMIT_SOFT (1 << 1)
#define PRLIMIT_HARD (1 << 2)
-/* array with IDs of enabled columns */
-static int columns[NCOLS], ncolumns;
static pid_t pid; /* calling process (default) */
static int verbose;
const struct rlimit *new_limit,
struct rlimit *old_limit)
{
- return syscall(SYS_prlimit, p, resource, new_limit, old_limit);
+ return syscall(SYS_prlimit64, p, resource, new_limit, old_limit);
}
#endif
fputs(USAGE_HEADER, out);
fprintf(out,
- _(" %s [options]\n"), program_invocation_short_name);
+ _(" %s [options] [-p PID]\n"), program_invocation_short_name);
+ fprintf(out,
+ _(" %s [options] COMMAND\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Show or change the resource limits of a process.\n"), out);
fputs(_("\nGeneral Options:\n"), out);
fputs(_(" -p, --pid <pid> process id\n"
" -d, --data maximum size of a process's data segment\n"
" -e, --nice maximum nice priority allowed to raise\n"
" -f, --fsize maximum size of files written by the process\n"
- " -i, --sigpending maximum amount of pending signals\n"
+ " -i, --sigpending maximum number of pending signals\n"
" -l, --memlock maximum size a process may lock into memory\n"
" -m, --rss maximum resident set size\n"
- " -n, --nofile maximum amount of open files\n"
+ " -n, --nofile maximum number of open files\n"
" -q, --msgqueue maximum bytes in POSIX message queues\n"
" -r, --rtprio maximum real-time scheduling priority\n"
" -s, --stack maximum stack size\n"
fputs(_("\nAvailable columns (for --output):\n"), out);
- for (i = 0; i < NCOLS; i++)
+ for (i = 0; i < ARRAY_SIZE(infos); i++)
fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
fprintf(out, USAGE_MAN_TAIL("prlimit(1)"));
static inline int get_column_id(int num)
{
- assert(ARRAY_SIZE(columns) == NCOLS);
assert(num < ncolumns);
- assert(columns[num] < (int) NCOLS);
+ assert(columns[num] < (int) ARRAY_SIZE(infos));
return columns[num];
}
return &infos[ get_column_id(num) ];
}
-static void add_tt_line(struct tt *tt, struct prlimit *l)
+static void add_scols_line(struct libscols_table *table, struct prlimit *l)
{
int i;
- struct tt_line *line;
+ struct libscols_line *line;
- assert(tt);
+ assert(table);
assert(l);
- line = tt_add_line(tt, NULL);
- if (!line) {
- warn(_("failed to add line to output"));
- return;
- }
+ line = scols_table_new_line(table, NULL);
+ if (!line)
+ err(EXIT_FAILURE, _("failed to allocate output line"));
for (i = 0; i < ncolumns; i++) {
char *str = NULL;
- int rc = 0;
switch (get_column_id(i)) {
case COL_RES:
- rc = asprintf(&str, "%s", l->desc->name);
+ str = xstrdup(l->desc->name);
break;
case COL_HELP:
- rc = asprintf(&str, "%s", l->desc->help);
+ str = xstrdup(l->desc->help);
break;
case COL_SOFT:
- rc = l->rlim.rlim_cur == RLIM_INFINITY ?
- asprintf(&str, "%s", "unlimited") :
- asprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_cur);
+ if (l->rlim.rlim_cur == RLIM_INFINITY)
+ str = xstrdup(_("unlimited"));
+ else
+ xasprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_cur);
break;
case COL_HARD:
- rc = l->rlim.rlim_max == RLIM_INFINITY ?
- asprintf(&str, "%s", "unlimited") :
- asprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_max);
+ if (l->rlim.rlim_max == RLIM_INFINITY)
+ str = xstrdup(_("unlimited"));
+ else
+ xasprintf(&str, "%llu", (unsigned long long) l->rlim.rlim_max);
break;
case COL_UNITS:
str = l->desc->unit ? xstrdup(_(l->desc->unit)) : NULL;
break;
}
- if (rc || str)
- tt_line_set_data(line, i, str);
+ if (str && scols_line_refer_data(line, i, str))
+ err(EXIT_FAILURE, _("failed to add output data"));
}
}
assert(name);
- for (i = 0; i < NCOLS; i++) {
+ for (i = 0; i < ARRAY_SIZE(infos); i++) {
const char *cn = infos[i].name;
if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
return -1;
}
-static int show_limits(struct prlimit lims[], size_t n, int tt_flags)
+static void rem_prlim(struct prlimit *lim)
+{
+ if (!lim)
+ return;
+ list_del(&lim->lims);
+ free(lim);
+}
+
+static int show_limits(struct list_head *lims)
{
int i;
- struct tt *tt;
+ struct list_head *p, *pnext;
+ struct libscols_table *table;
- tt = tt_new_table(tt_flags);
- if (!tt) {
- warn(_("failed to initialize output table"));
- return -1;
- }
+ table = scols_new_table();
+ if (!table)
+ err(EXIT_FAILURE, _("failed to allocate output table"));
+
+ scols_table_enable_raw(table, raw);
+ scols_table_enable_noheadings(table, no_headings);
for (i = 0; i < ncolumns; i++) {
struct colinfo *col = get_column_info(i);
- if (!tt_define_column(tt, col->name, col->whint, col->flags)) {
- warnx(_("failed to initialize output column"));
- goto done;
- }
+ if (!scols_table_new_column(table, col->name, col->whint, col->flags))
+ err(EXIT_FAILURE, _("failed to allocate output column"));
}
- for (i = 0; (size_t) i < n; i++)
- if (!lims[i].modify) /* only display old limits */
- add_tt_line(tt, &lims[i]);
+ list_for_each_safe(p, pnext, lims) {
+ struct prlimit *lim = list_entry(p, struct prlimit, lims);
+
+ add_scols_line(table, lim);
+ rem_prlim(lim);
+ }
- tt_print_table(tt);
-done:
- tt_free_table(tt);
+ scols_print_table(table);
+ scols_unref_table(table);
return 0;
}
+/*
+ * If one of the limits is unknown (default value for not being passed), we
+ * need to get the current limit and use it. I see no other way other than
+ * using prlimit(2).
+ */
+static void get_unknown_hardsoft(struct prlimit *lim)
+{
+ struct rlimit old;
+
+ if (prlimit(pid, lim->desc->resource, NULL, &old) == -1)
+ err(EXIT_FAILURE, _("failed to get old %s limit"),
+ lim->desc->name);
+
+ if (!(lim->modify & PRLIMIT_SOFT))
+ lim->rlim.rlim_cur = old.rlim_cur;
+ else if (!(lim->modify & PRLIMIT_HARD))
+ lim->rlim.rlim_max = old.rlim_max;
+}
-static void do_prlimit(struct prlimit lims[], size_t n, int tt_flags)
+static void do_prlimit(struct list_head *lims)
{
- size_t i, nshows = 0;
+ struct list_head *p, *pnext;
- for (i = 0; i < n; i++) {
- struct rlimit *new = NULL;
+ list_for_each_safe(p, pnext, lims) {
+ struct rlimit *new = NULL, *old = NULL;
+ struct prlimit *lim = list_entry(p, struct prlimit, lims);
- if (lims[i].modify)
- new = &lims[i].rlim;
- else
- nshows++;
+ if (lim->modify) {
+ if (lim->modify != (PRLIMIT_HARD | PRLIMIT_SOFT))
+ get_unknown_hardsoft(lim);
+
+ if ((lim->rlim.rlim_cur > lim->rlim.rlim_max) &&
+ (lim->rlim.rlim_cur != RLIM_INFINITY ||
+ lim->rlim.rlim_max != RLIM_INFINITY))
+ errx(EXIT_FAILURE, _("the soft limit %s cannot exceed the hard limit"),
+ lim->desc->name);
+ new = &lim->rlim;
+ } else
+ old = &lim->rlim;
if (verbose && new) {
- printf(_("New %s limit: "), lims[i].desc->name);
+ printf(_("New %s limit for pid %d: "), lim->desc->name,
+ pid ? pid : getpid());
if (new->rlim_cur == RLIM_INFINITY)
printf("<%s", _("unlimited"));
else
printf(":%ju>\n", new->rlim_max);
}
- if (prlimit(pid, lims[i].desc->resource, new, &lims[i].rlim) == -1)
- err(EXIT_FAILURE, _("failed to get resource limits for PID %d"), pid);
- }
+ if (prlimit(pid, lim->desc->resource, new, old) == -1)
+ err(EXIT_FAILURE, lim->modify ?
+ _("failed to set the %s resource limit") :
+ _("failed to get the %s resource limit"),
+ lim->desc->name);
- if (nshows)
- show_limits(lims, n, tt_flags);
+ if (lim->modify)
+ rem_prlim(lim); /* modify only; don't show */
+ }
}
-
-
static int get_range(char *str, rlim_t *soft, rlim_t *hard, int *found)
{
char *end = NULL;
errx(EXIT_FAILURE, _("failed to parse %s limit"),
prlimit_desc[id].name);
- /*
- * If one of the limits is unknown (default value for not being passed), we need
- * to get the current limit and use it.
- * I see no other way other than using prlimit(2).
- */
- if (found != (PRLIMIT_HARD | PRLIMIT_SOFT)) {
- struct rlimit old;
-
- if (prlimit(pid, prlimit_desc[id].resource, NULL, &old) == -1)
- errx(EXIT_FAILURE, _("failed to get old %s limit"),
- prlimit_desc[id].name);
-
- if (!(found & PRLIMIT_SOFT))
- soft = old.rlim_cur;
- else if (!(found & PRLIMIT_HARD))
- hard = old.rlim_max;
- }
-
- if (soft > hard && (soft != RLIM_INFINITY || hard != RLIM_INFINITY))
- errx(EXIT_FAILURE, _("the soft limit cannot exceed the ceiling value"));
-
lim->rlim_cur = soft;
lim->rlim_max = hard;
- return 0;
+ return found;
}
-/*
- * Add a resource limit to the limits array
- */
-static int add_prlim(char *ops, struct prlimit *lim, size_t id)
+static int add_prlim(char *ops, struct list_head *lims, size_t id)
{
+ struct prlimit *lim = xcalloc(1, sizeof(*lim));
+
+ INIT_LIST_HEAD(&lim->lims);
lim->desc = &prlimit_desc[id];
- if (ops) { /* planning on modifying a limit? */
- lim->modify = 1;
- parse_prlim(&lim->rlim, ops, id);
- }
+ if (ops)
+ lim->modify = parse_prlim(&lim->rlim, ops, id);
+ list_add_tail(&lim->lims, lims);
return 0;
}
int main(int argc, char **argv)
{
- int opt, tt_flags = 0;
- size_t n = 0;
- struct prlimit lims[MAX_RESOURCES] = { PRLIMIT_EMPTY_LIMIT };
+ int opt;
+ struct list_head lims;
enum {
VERBOSE_OPTION = CHAR_MAX + 1,
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ INIT_LIST_HEAD(&lims);
/*
* Something is very wrong if this doesn't succeed,
assert(MAX_RESOURCES == STACK + 1);
while((opt = getopt_long(argc, argv,
- "c::d::e::f::i::l::m::n::q::r::s::t::u::v::x::y::p:o:vVh",
+ "+c::d::e::f::i::l::m::n::q::r::s::t::u::v::x::y::p:o:vVh",
longopts, NULL)) != -1) {
switch(opt) {
case 'c':
- add_prlim(optarg, &lims[n++], CORE);
+ add_prlim(optarg, &lims, CORE);
break;
case 'd':
- add_prlim(optarg, &lims[n++], DATA);
+ add_prlim(optarg, &lims, DATA);
break;
case 'e':
- add_prlim(optarg, &lims[n++], NICE);
+ add_prlim(optarg, &lims, NICE);
break;
case 'f':
- add_prlim(optarg, &lims[n++], FSIZE);
+ add_prlim(optarg, &lims, FSIZE);
break;
case 'i':
- add_prlim(optarg, &lims[n++], SIGPENDING);
+ add_prlim(optarg, &lims, SIGPENDING);
break;
case 'l':
- add_prlim(optarg, &lims[n++], MEMLOCK);
+ add_prlim(optarg, &lims, MEMLOCK);
break;
case 'm':
- add_prlim(optarg, &lims[n++], RSS);
+ add_prlim(optarg, &lims, RSS);
break;
case 'n':
- add_prlim(optarg, &lims[n++], NOFILE);
+ add_prlim(optarg, &lims, NOFILE);
break;
case 'q':
- add_prlim(optarg, &lims[n++], MSGQUEUE);
+ add_prlim(optarg, &lims, MSGQUEUE);
break;
case 'r':
- add_prlim(optarg, &lims[n++], RTPRIO);
+ add_prlim(optarg, &lims, RTPRIO);
break;
case 's':
- add_prlim(optarg, &lims[n++], STACK);
+ add_prlim(optarg, &lims, STACK);
break;
case 't':
- add_prlim(optarg, &lims[n++], CPU);
+ add_prlim(optarg, &lims, CPU);
break;
case 'u':
- add_prlim(optarg, &lims[n++], NPROC);
+ add_prlim(optarg, &lims, NPROC);
break;
case 'v':
- add_prlim(optarg, &lims[n++], AS);
+ add_prlim(optarg, &lims, AS);
break;
case 'x':
- add_prlim(optarg, &lims[n++], LOCKS);
+ add_prlim(optarg, &lims, LOCKS);
break;
case 'y':
- add_prlim(optarg, &lims[n++], RTTIME);
+ add_prlim(optarg, &lims, RTTIME);
break;
case 'p':
- if (pid) /* we only work one pid at a time */
- errx(EXIT_FAILURE, _("only use one PID at a time"));
-
- pid = strtol_or_err(optarg, _("cannot parse PID"));
+ if (pid)
+ errx(EXIT_FAILURE, _("option --pid may be specified only once"));
+ pid = strtos32_or_err(optarg, _("invalid PID argument"));
break;
case 'h':
usage(stdout);
- break;
case 'o':
ncolumns = string_to_idarray(optarg,
columns, ARRAY_SIZE(columns),
return EXIT_SUCCESS;
case NOHEADINGS_OPTION:
- tt_flags |= TT_FL_NOHEADINGS;
+ no_headings = 1;
break;
case VERBOSE_OPTION:
verbose++;
break;
case RAW_OPTION:
- tt_flags |= TT_FL_RAW;
+ raw = 1;
break;
-
default:
- usage(stderr);
- break;
+ errtryhelp(EXIT_FAILURE);
}
}
-
- if (argc == 1)
- usage(stderr);
-
+ if (argc > optind && pid)
+ errx(EXIT_FAILURE, _("options --pid and COMMAND are mutually exclusive"));
if (!ncolumns) {
/* default columns */
columns[ncolumns++] = COL_RES;
columns[ncolumns++] = COL_UNITS;
}
- if (!n) {
+ scols_init_debug(0);
+
+ if (list_empty(&lims)) {
/* default is to print all resources */
- for (; n < MAX_RESOURCES; n++)
- add_prlim(NULL, &lims[n], n);
+ size_t n;
+
+ for (n = 0; n < MAX_RESOURCES; n++)
+ add_prlim(NULL, &lims, n);
}
- do_prlimit(lims, n, tt_flags);
+ do_prlimit(&lims);
+
+ if (!list_empty(&lims))
+ show_limits(&lims);
+
+ if (argc > optind) {
+ /* prlimit [options] COMMAND */
+ execvp(argv[optind], &argv[optind]);
+ err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]);
+ }
return EXIT_SUCCESS;
}