summaryrefslogtreecommitdiff
path: root/corebinutils/kill/kill.c
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:26:05 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:26:05 +0300
commit997dd21c2f45e9a586e9ef459381de1d540a9cfb (patch)
tree5648d5924a91841c15ae95b9e7b56a9b07a9b284 /corebinutils/kill/kill.c
parent3d884ae8958e9e060e76d6e8165eb33ae86829d4 (diff)
parent4888cfe1b3ad8e83935dc7b061bdbdade5d3ebfd (diff)
downloadProject-Tick-997dd21c2f45e9a586e9ef459381de1d540a9cfb.tar.gz
Project-Tick-997dd21c2f45e9a586e9ef459381de1d540a9cfb.zip
Add 'corebinutils/kill/' from commit '4888cfe1b3ad8e83935dc7b061bdbdade5d3ebfd'
git-subtree-dir: corebinutils/kill git-subtree-mainline: 3d884ae8958e9e060e76d6e8165eb33ae86829d4 git-subtree-split: 4888cfe1b3ad8e83935dc7b061bdbdade5d3ebfd
Diffstat (limited to 'corebinutils/kill/kill.c')
-rw-r--r--corebinutils/kill/kill.c575
1 files changed, 575 insertions, 0 deletions
diff --git a/corebinutils/kill/kill.c b/corebinutils/kill/kill.c
new file mode 100644
index 0000000000..82d8324365
--- /dev/null
+++ b/corebinutils/kill/kill.c
@@ -0,0 +1,575 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2026
+ * Project Tick. All rights reserved.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * 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.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef SHELL
+#define main killcmd
+#include "bltin/bltin.h"
+#include "error.h"
+#endif
+
+struct signal_entry {
+ const char *name;
+ int number;
+};
+
+#define SIGNAL_ENTRY(name) { #name, SIG##name }
+#define SIGNAL_ALIAS(name, signal) { name, signal }
+
+static const struct signal_entry canonical_signals[] = {
+#ifdef SIGHUP
+ SIGNAL_ENTRY(HUP),
+#endif
+#ifdef SIGINT
+ SIGNAL_ENTRY(INT),
+#endif
+#ifdef SIGQUIT
+ SIGNAL_ENTRY(QUIT),
+#endif
+#ifdef SIGILL
+ SIGNAL_ENTRY(ILL),
+#endif
+#ifdef SIGTRAP
+ SIGNAL_ENTRY(TRAP),
+#endif
+#ifdef SIGABRT
+ SIGNAL_ENTRY(ABRT),
+#endif
+#ifdef SIGBUS
+ SIGNAL_ENTRY(BUS),
+#endif
+#ifdef SIGFPE
+ SIGNAL_ENTRY(FPE),
+#endif
+#ifdef SIGKILL
+ SIGNAL_ENTRY(KILL),
+#endif
+#ifdef SIGUSR1
+ SIGNAL_ENTRY(USR1),
+#endif
+#ifdef SIGSEGV
+ SIGNAL_ENTRY(SEGV),
+#endif
+#ifdef SIGUSR2
+ SIGNAL_ENTRY(USR2),
+#endif
+#ifdef SIGPIPE
+ SIGNAL_ENTRY(PIPE),
+#endif
+#ifdef SIGALRM
+ SIGNAL_ENTRY(ALRM),
+#endif
+#ifdef SIGTERM
+ SIGNAL_ENTRY(TERM),
+#endif
+#ifdef SIGSTKFLT
+ SIGNAL_ENTRY(STKFLT),
+#endif
+#ifdef SIGCHLD
+ SIGNAL_ENTRY(CHLD),
+#endif
+#ifdef SIGCONT
+ SIGNAL_ENTRY(CONT),
+#endif
+#ifdef SIGSTOP
+ SIGNAL_ENTRY(STOP),
+#endif
+#ifdef SIGTSTP
+ SIGNAL_ENTRY(TSTP),
+#endif
+#ifdef SIGTTIN
+ SIGNAL_ENTRY(TTIN),
+#endif
+#ifdef SIGTTOU
+ SIGNAL_ENTRY(TTOU),
+#endif
+#ifdef SIGURG
+ SIGNAL_ENTRY(URG),
+#endif
+#ifdef SIGXCPU
+ SIGNAL_ENTRY(XCPU),
+#endif
+#ifdef SIGXFSZ
+ SIGNAL_ENTRY(XFSZ),
+#endif
+#ifdef SIGVTALRM
+ SIGNAL_ENTRY(VTALRM),
+#endif
+#ifdef SIGPROF
+ SIGNAL_ENTRY(PROF),
+#endif
+#ifdef SIGWINCH
+ SIGNAL_ENTRY(WINCH),
+#endif
+#ifdef SIGIO
+ SIGNAL_ENTRY(IO),
+#endif
+#ifdef SIGPWR
+ SIGNAL_ENTRY(PWR),
+#endif
+#ifdef SIGSYS
+ SIGNAL_ENTRY(SYS),
+#endif
+};
+
+static const struct signal_entry signal_aliases[] = {
+#ifdef SIGIOT
+ SIGNAL_ALIAS("IOT", SIGIOT),
+#endif
+#ifdef SIGCLD
+ SIGNAL_ALIAS("CLD", SIGCLD),
+#endif
+#ifdef SIGPOLL
+ SIGNAL_ALIAS("POLL", SIGPOLL),
+#endif
+#ifdef SIGUNUSED
+ SIGNAL_ALIAS("UNUSED", SIGUNUSED),
+#endif
+};
+
+static void usage(void);
+static void die(int status, const char *fmt, ...);
+static char *normalize_signal_name(const char *token);
+static bool parse_pid_argument(const char *text, pid_t *pid);
+static bool parse_nonnegative_number(const char *text, int *value);
+static bool parse_signal_name(const char *token, int *signum);
+static bool parse_signal_option_token(const char *token, int *signum);
+static bool parse_signal_for_dash_s(const char *token, int *signum);
+static bool signal_name_for_number(int signum, char *buffer, size_t buffer_size);
+static void printsignals(FILE *fp);
+static void unknown_signal(const char *token);
+static int max_signal_number(void);
+
+static void
+usage(void)
+{
+#ifdef SHELL
+ error("usage: kill [-s signal_name] pid ...");
+#else
+ fprintf(stderr,
+ "usage: kill [-s signal_name] pid ...\n"
+ " kill -l [exit_status]\n"
+ " kill -signal_name pid ...\n"
+ " kill -signal_number pid ...\n");
+ exit(2);
+#endif
+}
+
+static void
+die(int status, const char *fmt, ...)
+{
+ va_list ap;
+
+#ifdef SHELL
+ char buffer[256];
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ va_end(ap);
+ errorwithstatus(status, "%s", buffer);
+#else
+ fprintf(stderr, "kill: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ exit(status);
+#endif
+}
+
+static char *
+normalize_signal_name(const char *token)
+{
+ size_t i, length, offset;
+ char *name;
+
+ length = strlen(token);
+ name = malloc(length + 1);
+ if (name == NULL)
+ die(2, "out of memory");
+
+ for (i = 0; i < length; i++)
+ name[i] = (char)toupper((unsigned char)token[i]);
+ name[length] = '\0';
+
+ offset = 0;
+ if (length > 3 && name[0] == 'S' && name[1] == 'I' && name[2] == 'G')
+ offset = 3;
+ if (offset != 0)
+ memmove(name, name + offset, length - offset + 1);
+
+ return (name);
+}
+
+static bool
+parse_pid_argument(const char *text, pid_t *pid)
+{
+ intmax_t value;
+ pid_t converted;
+ char *end;
+
+ if (text[0] == '\0')
+ return (false);
+
+ errno = 0;
+ value = strtoimax(text, &end, 10);
+ if (errno == ERANGE || *end != '\0')
+ return (false);
+
+ converted = (pid_t)value;
+ if ((intmax_t)converted != value)
+ return (false);
+
+ *pid = converted;
+ return (true);
+}
+
+static bool
+parse_nonnegative_number(const char *text, int *value)
+{
+ intmax_t parsed;
+ char *end;
+
+ if (text[0] == '\0')
+ return (false);
+
+ errno = 0;
+ parsed = strtoimax(text, &end, 10);
+ if (errno == ERANGE || *end != '\0' || parsed < 0 || parsed > INT_MAX)
+ return (false);
+
+ *value = (int)parsed;
+ return (true);
+}
+
+static bool
+parse_signal_name(const char *token, int *signum)
+{
+ char *name;
+ char *end;
+ int offset;
+ size_t i;
+
+ if (strcmp(token, "0") == 0) {
+ *signum = 0;
+ return (true);
+ }
+
+ name = normalize_signal_name(token);
+ for (i = 0; i < sizeof(canonical_signals) / sizeof(canonical_signals[0]); i++) {
+ if (strcmp(name, canonical_signals[i].name) == 0) {
+ *signum = canonical_signals[i].number;
+ free(name);
+ return (true);
+ }
+ }
+
+ for (i = 0; i < sizeof(signal_aliases) / sizeof(signal_aliases[0]); i++) {
+ if (strcmp(name, signal_aliases[i].name) == 0) {
+ *signum = signal_aliases[i].number;
+ free(name);
+ return (true);
+ }
+ }
+
+#ifdef SIGRTMIN
+#ifdef SIGRTMAX
+ if (strcmp(name, "RTMIN") == 0) {
+ *signum = SIGRTMIN;
+ free(name);
+ return (true);
+ }
+ if (strncmp(name, "RTMIN+", 6) == 0) {
+ errno = 0;
+ offset = (int)strtol(name + 6, &end, 10);
+ if (errno == 0 && *end == '\0' && offset >= 0 &&
+ SIGRTMIN + offset <= SIGRTMAX) {
+ *signum = SIGRTMIN + offset;
+ free(name);
+ return (true);
+ }
+ }
+ if (strcmp(name, "RTMAX") == 0) {
+ *signum = SIGRTMAX;
+ free(name);
+ return (true);
+ }
+ if (strncmp(name, "RTMAX-", 6) == 0) {
+ errno = 0;
+ offset = (int)strtol(name + 6, &end, 10);
+ if (errno == 0 && *end == '\0' && offset >= 0 &&
+ SIGRTMAX - offset >= SIGRTMIN) {
+ *signum = SIGRTMAX - offset;
+ free(name);
+ return (true);
+ }
+ }
+#endif
+#endif
+
+ free(name);
+ return (false);
+}
+
+static bool
+parse_signal_option_token(const char *token, int *signum)
+{
+ if (parse_nonnegative_number(token, signum))
+ return (true);
+ return (parse_signal_name(token, signum));
+}
+
+static bool
+parse_signal_for_dash_s(const char *token, int *signum)
+{
+ if (parse_nonnegative_number(token, signum))
+ return (true);
+ return (parse_signal_name(token, signum));
+}
+
+static bool
+signal_name_for_number(int signum, char *buffer, size_t buffer_size)
+{
+ size_t i;
+
+ if (signum == 0) {
+ if (buffer != NULL && buffer_size > 0) {
+ buffer[0] = '0';
+ if (buffer_size > 1)
+ buffer[1] = '\0';
+ }
+ return (true);
+ }
+
+ for (i = 0; i < sizeof(canonical_signals) / sizeof(canonical_signals[0]); i++) {
+ if (canonical_signals[i].number != signum)
+ continue;
+ if (buffer != NULL && buffer_size > 0) {
+ if (snprintf(buffer, buffer_size, "%s",
+ canonical_signals[i].name) >= (int)buffer_size)
+ die(2, "internal signal name buffer overflow");
+ }
+ return (true);
+ }
+
+#ifdef SIGRTMIN
+#ifdef SIGRTMAX
+ if (signum == SIGRTMIN) {
+ if (buffer != NULL && buffer_size > 0)
+ snprintf(buffer, buffer_size, "RTMIN");
+ return (true);
+ }
+ if (signum == SIGRTMAX) {
+ if (buffer != NULL && buffer_size > 0)
+ snprintf(buffer, buffer_size, "RTMAX");
+ return (true);
+ }
+ if (signum > SIGRTMIN && signum < SIGRTMAX) {
+ if (buffer != NULL && buffer_size > 0) {
+ if (snprintf(buffer, buffer_size, "RTMIN+%d",
+ signum - SIGRTMIN) >= (int)buffer_size)
+ die(2, "internal signal name buffer overflow");
+ }
+ return (true);
+ }
+#endif
+#endif
+
+ return (false);
+}
+
+static void
+printsignals(FILE *fp)
+{
+ char signame[32];
+ int line_length, maxsig, signum;
+
+ line_length = 0;
+ maxsig = max_signal_number();
+ for (signum = 1; signum <= maxsig; signum++) {
+ size_t name_length;
+
+ if (!signal_name_for_number(signum, signame, sizeof(signame)))
+ continue;
+
+ name_length = strlen(signame);
+ if (line_length != 0) {
+ if (line_length + 1 + (int)name_length > 72) {
+ putc('\n', fp);
+ line_length = 0;
+ } else {
+ putc(' ', fp);
+ line_length++;
+ }
+ }
+ fputs(signame, fp);
+ line_length += (int)name_length;
+ }
+ putc('\n', fp);
+}
+
+static void
+unknown_signal(const char *token)
+{
+ fprintf(stderr, "kill: unknown signal %s; valid signals:\n", token);
+ printsignals(stderr);
+ exit(2);
+}
+
+static int
+max_signal_number(void)
+{
+ size_t i;
+ int maxsig;
+
+ maxsig = 0;
+ for (i = 0; i < sizeof(canonical_signals) / sizeof(canonical_signals[0]); i++) {
+ if (canonical_signals[i].number > maxsig)
+ maxsig = canonical_signals[i].number;
+ }
+#ifdef SIGRTMAX
+ if (SIGRTMAX > maxsig)
+ maxsig = SIGRTMAX;
+#endif
+ return (maxsig);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char signame[32];
+ pid_t pid;
+ int errors, signum, status_value;
+
+ if (argc < 2)
+ usage();
+
+ signum = SIGTERM;
+ argc--;
+ argv++;
+
+ if (strcmp(argv[0], "-l") == 0) {
+ argc--;
+ argv++;
+ if (argc > 1)
+ usage();
+ if (argc == 0) {
+ printsignals(stdout);
+ return (0);
+ }
+
+ if (parse_nonnegative_number(argv[0], &status_value)) {
+ if (status_value >= 128)
+ status_value -= 128;
+ if (status_value == 0) {
+ printf("0\n");
+ return (0);
+ }
+ if (!signal_name_for_number(status_value, signame, sizeof(signame)))
+ unknown_signal(argv[0]);
+ printf("%s\n", signame);
+ return (0);
+ }
+ if (!parse_signal_name(argv[0], &status_value))
+ unknown_signal(argv[0]);
+ printf("%d\n", status_value);
+ return (0);
+ }
+
+ if (strcmp(argv[0], "-s") == 0) {
+ argc--;
+ argv++;
+ if (argc == 0) {
+ fprintf(stderr, "kill: option requires an argument -- s\n");
+ usage();
+ }
+ if (!parse_signal_for_dash_s(argv[0], &signum))
+ die(2, "option -s requires a signal name, number, or 0: %s",
+ argv[0]);
+ argc--;
+ argv++;
+ } else if (argv[0][0] == '-' && argv[0][1] != '\0' && argv[0][1] != '-') {
+ if (!parse_signal_option_token(argv[0] + 1, &signum))
+ unknown_signal(argv[0] + 1);
+ argc--;
+ argv++;
+ }
+
+ if (argc > 0 && strcmp(argv[0], "--") == 0) {
+ argc--;
+ argv++;
+ }
+
+ if (argc == 0)
+ usage();
+
+ errors = 0;
+ for (; argc > 0; argc--, argv++) {
+ if (argv[0][0] == '%') {
+#ifdef SHELL
+ if (killjob(argv[0], signum) != 0) {
+ fprintf(stderr, "kill: %s: %s\n", argv[0],
+ strerror(errno));
+ errors = 1;
+ }
+ continue;
+#else
+ die(2, "job control process specifications require a shell builtin: %s",
+ argv[0]);
+#endif
+ }
+ if (!parse_pid_argument(argv[0], &pid))
+ die(2, "illegal process id: %s", argv[0]);
+ if (kill(pid, signum) != 0) {
+ fprintf(stderr, "kill: %s: %s\n", argv[0], strerror(errno));
+ errors = 1;
+ }
+ }
+
+ return (errors);
+}