diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:27:27 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:27:27 +0300 |
| commit | 294b9fe361076c6c740bc182dc7605e745ed2ea3 (patch) | |
| tree | 75eec75937af0579c7e5e0c8e8a02260d0df83d0 /corebinutils/nproc/nproc.c | |
| parent | 5086370dd0d4e581d1f5e4b8a085cd421eb23235 (diff) | |
| parent | b02e14aacf37412ca3632719075c9c42967e23b6 (diff) | |
| download | Project-Tick-294b9fe361076c6c740bc182dc7605e745ed2ea3.tar.gz Project-Tick-294b9fe361076c6c740bc182dc7605e745ed2ea3.zip | |
Add 'corebinutils/nproc/' from commit 'b02e14aacf37412ca3632719075c9c42967e23b6'
git-subtree-dir: corebinutils/nproc
git-subtree-mainline: 5086370dd0d4e581d1f5e4b8a085cd421eb23235
git-subtree-split: b02e14aacf37412ca3632719075c9c42967e23b6
Diffstat (limited to 'corebinutils/nproc/nproc.c')
| -rw-r--r-- | corebinutils/nproc/nproc.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/corebinutils/nproc/nproc.c b/corebinutils/nproc/nproc.c new file mode 100644 index 0000000000..c2e01e421e --- /dev/null +++ b/corebinutils/nproc/nproc.c @@ -0,0 +1,343 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Mateusz Guzik + * Copyright (c) 2026 Project Tick + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <sched.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +enum command_mode { + MODE_RUN, + MODE_HELP, + MODE_VERSION +}; + +struct options { + enum command_mode mode; + bool all; + uintmax_t ignore; +}; + +static const char *progname = "nproc"; + +static void die(const char *fmt, ...) __attribute__((__noreturn__, format(printf, 1, 2))); +static void die_errno(const char *what) __attribute__((__noreturn__)); +static void help(FILE *stream); +static size_t available_processors(void); +static size_t count_set_bits(const unsigned char *mask, size_t mask_size); +static size_t initial_mask_size(void); +static size_t online_processors(void); +static void parse_args(int argc, char **argv, struct options *options); +static uintmax_t parse_ignore_count(const char *text); +static size_t subtract_ignore(size_t count, uintmax_t ignore); +static void usage_error(const char *fmt, ...) __attribute__((__noreturn__, format(printf, 1, 2))); +static void version(void); + +int +main(int argc, char **argv) +{ + struct options options; + size_t count; + + if (argv[0] != NULL && argv[0][0] != '\0') { + const char *slash; + + slash = strrchr(argv[0], '/'); + progname = (slash != NULL && slash[1] != '\0') ? slash + 1 : argv[0]; + } + + parse_args(argc, argv, &options); + + switch (options.mode) { + case MODE_HELP: + help(stderr); + return (EXIT_SUCCESS); + case MODE_VERSION: + version(); + return (EXIT_SUCCESS); + case MODE_RUN: + break; + } + + count = options.all ? online_processors() : available_processors(); + count = subtract_ignore(count, options.ignore); + + if (printf("%zu\n", count) < 0) + die_errno("stdout"); + + return (EXIT_SUCCESS); +} + +static void +parse_args(int argc, char **argv, struct options *options) +{ + int i; + + memset(options, 0, sizeof(*options)); + options->mode = MODE_RUN; + + for (i = 1; i < argc; i++) { + const char *arg; + + arg = argv[i]; + if (strcmp(arg, "--") == 0) { + i++; + break; + } + if (strcmp(arg, "--all") == 0) { + options->all = true; + continue; + } + if (strcmp(arg, "--help") == 0) { + if (argc != 2) + usage_error("option --help must be used alone"); + options->mode = MODE_HELP; + return; + } + if (strcmp(arg, "--version") == 0) { + if (argc != 2) + usage_error("option --version must be used alone"); + options->mode = MODE_VERSION; + return; + } + if (strcmp(arg, "--ignore") == 0) { + if (i + 1 >= argc) + usage_error("option requires an argument: --ignore"); + i++; + options->ignore = parse_ignore_count(argv[i]); + continue; + } + if (strncmp(arg, "--ignore=", 9) == 0) { + options->ignore = parse_ignore_count(arg + 9); + continue; + } + if (arg[0] == '-') { + usage_error("unknown option: %s", arg); + } + break; + } + + if (i < argc) + usage_error("unexpected operand: %s", argv[i]); +} + +static uintmax_t +parse_ignore_count(const char *text) +{ + char *end; + uintmax_t value; + const unsigned char *p; + + if (text[0] == '\0') + die("invalid ignore count: %s", text); + + for (p = (const unsigned char *)text; *p != '\0'; p++) { + if (!isdigit(*p)) + die("invalid ignore count: %s", text); + } + + errno = 0; + value = strtoumax(text, &end, 10); + if (errno == ERANGE || *end != '\0') + die("invalid ignore count: %s", text); + return (value); +} + +static size_t +online_processors(void) +{ + long value; + + errno = 0; + value = sysconf(_SC_NPROCESSORS_ONLN); + if (value < 1) { + if (errno != 0) + die_errno("sysconf(_SC_NPROCESSORS_ONLN)"); + die("sysconf(_SC_NPROCESSORS_ONLN) returned %ld", value); + } + + return ((size_t)value); +} + +static size_t +initial_mask_size(void) +{ + long configured; + size_t bits; + size_t bytes; + + errno = 0; + configured = sysconf(_SC_NPROCESSORS_CONF); + if (configured < 1 || configured > LONG_MAX / 2) { + if (errno != 0) + return (128); + return (128); + } + + bits = (size_t)configured; + if (bits < CHAR_BIT) + bits = CHAR_BIT; + + bytes = bits / CHAR_BIT; + if ((bits % CHAR_BIT) != 0) + bytes++; + if (bytes < sizeof(unsigned long)) + bytes = sizeof(unsigned long); + return (bytes); +} + +static size_t +available_processors(void) +{ + unsigned char *mask; + size_t count; + size_t mask_size; + + mask_size = initial_mask_size(); + + for (;;) { + int saved_errno; + + mask = calloc(1, mask_size); + if (mask == NULL) + die_errno("calloc"); + + if (sched_getaffinity(0, mask_size, (cpu_set_t *)(void *)mask) == 0) { + count = count_set_bits(mask, mask_size); + free(mask); + if (count == 0) + die("sched_getaffinity returned an empty CPU mask"); + return (count); + } + + saved_errno = errno; + free(mask); + if (saved_errno != EINVAL) { + errno = saved_errno; + die_errno("sched_getaffinity"); + } + if (mask_size > SIZE_MAX / 2) + die("sched_getaffinity CPU mask is too large"); + mask_size *= 2; + } +} + +static size_t +count_set_bits(const unsigned char *mask, size_t mask_size) +{ + size_t count; + size_t i; + + count = 0; + for (i = 0; i < mask_size; i++) { + unsigned int bits; + + bits = mask[i]; + while (bits != 0) { + count++; + bits &= bits - 1; + } + } + + return (count); +} + +static size_t +subtract_ignore(size_t count, uintmax_t ignore) +{ + if (ignore >= count) + return (1); + return (count - (size_t)ignore); +} + +static void +help(FILE *stream) +{ + if (fprintf(stream, + "usage: %s [--all] [--ignore=count]\n" + " %s --help\n" + " %s --version\n", + progname, progname, progname) < 0) { + if (stream == stdout) + die_errno("stdout"); + die_errno("stderr"); + } +} + +static void +version(void) +{ + if (printf("%s (neither_GNU nor_coreutils) 8.32\n", progname) < 0) + die_errno("stdout"); +} + +static void +usage_error(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "%s: ", progname); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fputc('\n', stderr); + help(stderr); + exit(EXIT_FAILURE); +} + +static void +die(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "%s: ", progname); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fputc('\n', stderr); + exit(EXIT_FAILURE); +} + +static void +die_errno(const char *what) +{ + fprintf(stderr, "%s: %s: %s\n", progname, what, strerror(errno)); + exit(EXIT_FAILURE); +} |
