]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/prlimit.c
misc: consolidate smartcols error messages
[thirdparty/util-linux.git] / sys-utils / prlimit.c
index ec794d53c3a23d3609fd0b2c7d7e62f9ecbb9ada..a62d457feb8875ca6ff2f0d31d89c6a5ca0baaaa 100644 (file)
@@ -13,9 +13,9 @@
  *  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
@@ -56,6 +59,10 @@ enum {
        STACK
 };
 
+/* basic output flags */
+static int no_headings;
+static int raw;
+
 struct prlimit_desc {
        const char *name;
        const char *help;
@@ -66,27 +73,31 @@ struct prlimit_desc {
 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 }
@@ -103,21 +114,23 @@ enum {
 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)
@@ -125,8 +138,6 @@ struct colinfo infos[] = {
 #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;
 
@@ -136,7 +147,7 @@ static int prlimit(pid_t p, int resource,
                   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
 
@@ -147,7 +158,12 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
        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"
@@ -163,10 +179,10 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
                " -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"
@@ -179,7 +195,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
 
        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)"));
@@ -189,9 +205,8 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
 
 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];
 }
@@ -201,40 +216,39 @@ static inline struct colinfo *get_column_info(unsigned 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;
@@ -243,8 +257,8 @@ static void add_tt_line(struct tt *tt, struct prlimit *l)
                        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"));
        }
 }
 
@@ -254,7 +268,7 @@ static int column_name_to_id(const char *name, size_t namesz)
 
        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))
@@ -264,51 +278,89 @@ static int column_name_to_id(const char *name, size_t 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
@@ -320,16 +372,17 @@ static void do_prlimit(struct prlimit lims[], size_t n, int tt_flags)
                                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;
@@ -402,53 +455,30 @@ static int parse_prlim(struct rlimit *lim, char *ops, size_t id)
                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,
@@ -486,6 +516,9 @@ int main(int argc, char **argv)
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
+       atexit(close_stdout);
+
+       INIT_LIST_HEAD(&lims);
 
        /*
         * Something is very wrong if this doesn't succeed,
@@ -494,67 +527,65 @@ int main(int argc, char **argv)
        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),
@@ -567,24 +598,20 @@ int main(int argc, char **argv)
                        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;
@@ -594,13 +621,26 @@ int main(int argc, char **argv)
                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;
 }