Add nproc(1)

This program prints the number of CPU threads it can run on, while
respecting cpusets (or not, depending on switches).

It aims to be compatible with nproc as found in GNU coreutils.

Reviewed by:	des
Reviewed by:	pstef
Differential Revision:	https://reviews.freebsd.org/D38386
This commit is contained in:
Mateusz Guzik 2023-02-04 23:33:48 +00:00
parent 647f2d2bc0
commit 48bfd35976
5 changed files with 192 additions and 0 deletions

View File

@ -24,6 +24,7 @@ SUBDIR= cat \
ls \
mkdir \
mv \
nproc \
pax \
pkill \
ps \

4
bin/nproc/Makefile Normal file
View File

@ -0,0 +1,4 @@
PACKAGE=runtime
PROG= nproc
.include <bsd.prog.mk>

54
bin/nproc/nproc.1 Normal file
View File

@ -0,0 +1,54 @@
.\"-
.\" * Copyright (c) 2023 Piotr Paweł Stefaniak
.\"
.\" * SPDX-License-Identifier: BSD-2-Clause
.\"
.Dd February 5, 2023
.Dt NPROC 1
.Os
.Sh NAME
.Nm nproc
.Nd print the number of processors
.Sh SYNOPSIS
.Nm
.Op Fl -all
.Op Fl -ignore Ns = Ns Ar count
.Nm Fl -help
.Nm Fl -version
.Sh DESCRIPTION
The
.Nm
utility is used to print the number of processors limited to the
.Xr cpuset 2
of the current process, unless the
.Fl -all
flag is specified.
.Pp
The available flags are:
.Bl -tag -width Ds
.It Fl -all
Count all processors currently online.
.It Fl -ignore Ns = Ns Ar count
The result is decreased by
.Ar count ,
but never below 1.
.It Fl -version
Print the current program version and exit. Don't use this option.
.It Fl -help
Print usage information and exit.
.El
.Sh COMPATIBILITY
This program is intended to be compatible with nproc as found in GNU coreutils.
.Sh SEE ALSO
.Xr cpuset 1
.Sh HISTORY
The
.Nm
utility first appeared in
.Fx 14.0 .
.Sh AUTHORS
.An -nosplit
.An Mateusz Guzik Aq Mt mjg@FreeBSD.org
wrote the program and
.An Piotr Paweł Stefaniak Aq Mt pstef@FreeBSD.org
wrote this page.

132
bin/nproc/nproc.c Normal file
View File

@ -0,0 +1,132 @@
/*-
* Copyright (c) 2023 Mateusz Guzik
*
* SPDX-License-Identifier: BSD-2-Clause
*/
/*
* This program is intended to be compatible with nproc as found in GNU
* coreutils.
*
* In order to maintain that, do not add any features here if they are not
* present in said program. If you are looking for anything more advanced you
* probably should patch cpuset(1) instead.
*/
#include <sys/param.h>
#include <sys/cpuset.h>
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#define OPT_ALL (CHAR_MAX + 1)
#define OPT_IGNORE (CHAR_MAX + 2)
#define OPT_VERSION (CHAR_MAX + 3)
#define OPT_HELP (CHAR_MAX + 4)
static struct option long_opts[] = {
{ "all", no_argument, NULL, OPT_ALL },
{ "ignore", required_argument, NULL, OPT_IGNORE },
{ "version", no_argument, NULL, OPT_VERSION },
{ "help", no_argument, NULL, OPT_HELP },
{ NULL, 0, NULL, 0 }
};
static void
help(void)
{
fprintf(stderr,
"usage: nproc [--all] [--ignore=count]\n");
fprintf(stderr,
" nproc --help\n");
fprintf(stderr,
" nproc --version\n");
}
static void
usage(void)
{
help();
exit(EX_USAGE);
}
/*
* GNU variant ships with the --version switch.
*
* While we don't have anything to put there, print something which is
* whitespace-compatible with the original. Version number was taken
* from coreutils this code is in sync with.
*/
static void
version(void)
{
printf("nproc (neither_GNU nor_coreutils) 8.32\n");
exit(EXIT_SUCCESS);
}
int
main(int argc, char *argv[])
{
const char *errstr;
cpuset_t mask;
int ch, cpus, ignore;
bool all_flag;
ignore = 0;
all_flag = false;
while ((ch = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
switch (ch) {
case OPT_ALL:
all_flag = true;
break;
case OPT_IGNORE:
ignore = strtonum(optarg, 0, INT_MAX, &errstr);
if (errstr)
errx(1, "bad ignore count: %s", errstr);
break;
case OPT_VERSION:
version();
__builtin_unreachable();
case OPT_HELP:
help();
exit(EXIT_SUCCESS);
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 0)
usage();
if (all_flag) {
cpus = sysconf(_SC_NPROCESSORS_ONLN);
if (cpus == -1)
err(1, "sysconf");
} else {
CPU_ZERO(&mask);
if (cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1,
sizeof(mask), &mask) != 0)
err(1, "cpuset_getaffinity");
cpus = CPU_COUNT(&mask);
}
if (ignore >= cpus)
cpus = 1;
else
cpus -= ignore;
printf("%u\n", cpus);
exit(EXIT_SUCCESS);
}

View File

@ -231,6 +231,7 @@ Create a new cpuset that is restricted to CPUs 0 and 2 and move
into the new set:
.Dl cpuset -C -c -l 0,2 -p <pid>
.Sh SEE ALSO
.Xr nproc 1 ,
.Xr cpuset 2 ,
.Xr rctl 8
.Sh HISTORY