mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
a6ecbb747d
This allocator shaves ~20kb off single-threaded tool programs and is slightly faster than proper malloc for simple non-demanding programs
233 lines
7.5 KiB
C
233 lines
7.5 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/dce.h"
|
|
#include "libc/limits.h"
|
|
#include "libc/macros.internal.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/nt/enum/formatmessageflags.h"
|
|
#include "libc/nt/enum/lang.h"
|
|
#include "libc/nt/enum/processaccess.h"
|
|
#include "libc/nt/process.h"
|
|
#include "libc/nt/runtime.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/stdio/internal.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/sysv/consts/sig.h"
|
|
#include "libc/x/x.h"
|
|
#include "third_party/getopt/getopt.internal.h"
|
|
|
|
/**
|
|
* @fileoverview Mass Process Killer for Windows.
|
|
*
|
|
* Bad things sometimes happen during development, such as fork bombs.
|
|
* Under such circumstances, the Windows operating system itself remains
|
|
* remarkably stable (much more so than Linux would in these cases) but
|
|
* the tools on Windows for managing processes do not scale gracefully;
|
|
* GUIs like the Task Manager and Process Explorer become unresponsive
|
|
* when the system has a nontrivial number of processes, leaving no way
|
|
* to kill the processes. This tool is designed to make it easy to kill
|
|
* processes, particularly in large numbers, via a simple CLI interface.
|
|
*/
|
|
|
|
static const char *prog;
|
|
static char16_t **filters;
|
|
static uint32_t pids[10000];
|
|
|
|
#include "libc/mem/tinymalloc.inc"
|
|
|
|
static wontreturn void PrintUsage(int rc, FILE *f) {
|
|
fprintf(f,
|
|
"Usage: %s [-nshv] NAME...\n"
|
|
"\tNAME\tcase-insensitive process name substring filter\n"
|
|
"\t-n\tdry run (print matching processes but do not kill)\n"
|
|
"\t-v\tverbose mode\n"
|
|
"\t-s\tsilent mode\n"
|
|
"\t-h\tshow help\n",
|
|
prog);
|
|
exit(rc);
|
|
}
|
|
|
|
static wontreturn void OutOfMemory(void) {
|
|
fprintf(stderr, "%s: out of memory\n", prog);
|
|
exit(1);
|
|
}
|
|
|
|
static void *Calloc(size_t n, size_t z) {
|
|
void *p;
|
|
if (!(p = calloc(n, z)))
|
|
OutOfMemory();
|
|
return p;
|
|
}
|
|
|
|
static void ConvertStringToLowercase16(char16_t *s) {
|
|
while (*s) {
|
|
*s = towlower(*s);
|
|
++s;
|
|
}
|
|
}
|
|
|
|
static bool ShouldKillProcess(char16_t *name) {
|
|
if (!*filters) {
|
|
return true;
|
|
}
|
|
for (char16_t **f = filters; *f; ++f) {
|
|
if (strstr16(name, *f)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int64_t MyOpenProcess(uint32_t pid) {
|
|
return OpenProcess(
|
|
kNtProcessTerminate | kNtProcessQueryInformation | kNtProcessVmRead,
|
|
false, pid);
|
|
}
|
|
|
|
static bool GetProcessName(int64_t hand, char16_t name[hasatleast PATH_MAX]) {
|
|
int64_t hMod;
|
|
uint32_t cbNeeded;
|
|
*name = 0;
|
|
if (EnumProcessModules(hand, &hMod, sizeof(hMod), &cbNeeded)) {
|
|
GetModuleBaseName(hand, hMod, name, PATH_MAX);
|
|
}
|
|
return !!name[0];
|
|
}
|
|
|
|
static char16_t *DescribeError(int err) {
|
|
static char16_t msg[256];
|
|
FormatMessage(kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, 0,
|
|
err, MAKELANGID(kNtLangNeutral, kNtSublangDefault), msg,
|
|
ARRAYLEN(msg), 0);
|
|
return chomp16(msg);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int i, j;
|
|
|
|
// get program name
|
|
prog = argv[0] ? argv[0] : "killall";
|
|
|
|
// sanity check environment
|
|
if (!IsWindows()) {
|
|
fprintf(stderr, "%s: this program is intended for windows\n", prog);
|
|
exit(1);
|
|
}
|
|
|
|
// try to minimize terminal writes slowing us down
|
|
setvbuf(stdin, NULL, _IONBF, 0);
|
|
|
|
// get flags
|
|
int opt;
|
|
bool dryrun = false;
|
|
bool silent = false;
|
|
bool verbose = false;
|
|
while ((opt = getopt(argc, argv, "nhsv")) != -1) {
|
|
switch (opt) {
|
|
case 'n':
|
|
dryrun = true;
|
|
break;
|
|
case 's':
|
|
silent = true;
|
|
break;
|
|
case 'v':
|
|
verbose = true;
|
|
break;
|
|
case 'h':
|
|
PrintUsage(0, stdout);
|
|
default:
|
|
PrintUsage(1, stderr);
|
|
}
|
|
}
|
|
|
|
// get names of things to kill
|
|
filters = Calloc(argc, sizeof(char16_t *));
|
|
for (j = 0, i = optind; i < argc; ++i) {
|
|
char16_t *filter;
|
|
if ((filter = utf8to16(argv[i], -1, 0)) && *filter) {
|
|
ConvertStringToLowercase16(filter);
|
|
filters[j++] = filter;
|
|
}
|
|
}
|
|
if (!j && !dryrun) {
|
|
fprintf(stderr, "%s: missing operand\n", prog);
|
|
exit(1);
|
|
}
|
|
|
|
// outer loop
|
|
int count = 0;
|
|
int subcount;
|
|
do {
|
|
|
|
// get pids of all processes on system
|
|
uint32_t n;
|
|
if (!EnumProcesses(pids, sizeof(pids), &n)) {
|
|
fprintf(stderr, "%s: EnumProcesses() failed: %hs\n", prog,
|
|
DescribeError(GetLastError()));
|
|
exit(1);
|
|
}
|
|
n /= sizeof(uint32_t);
|
|
|
|
// kill matching processes
|
|
int64_t hProcess;
|
|
char16_t name[PATH_MAX];
|
|
for (subcount = i = 0; i < n; i++) {
|
|
if (!pids[i])
|
|
continue;
|
|
if ((hProcess = MyOpenProcess(pids[i]))) {
|
|
if (GetProcessName(hProcess, name)) {
|
|
ConvertStringToLowercase16(name);
|
|
if (ShouldKillProcess(name)) {
|
|
if (dryrun) {
|
|
if (!silent) {
|
|
printf("%5u %hs\n", pids[i], name);
|
|
}
|
|
++subcount;
|
|
} else if (TerminateProcess(hProcess, SIGKILL)) {
|
|
if (!silent) {
|
|
printf("%5u %hs killed\n", pids[i], name);
|
|
}
|
|
++subcount;
|
|
} else {
|
|
printf("%5u %hs %hs\n", pids[i], name,
|
|
DescribeError(GetLastError()));
|
|
}
|
|
}
|
|
} else if (verbose) {
|
|
fprintf(stderr, "%s: GetProcessName(%u) failed: %hs\n", prog, pids[i],
|
|
DescribeError(GetLastError()));
|
|
}
|
|
CloseHandle(hProcess);
|
|
} else if (verbose) {
|
|
fprintf(stderr, "%s: OpenProcess(%u) failed: %hs\n", prog, pids[i],
|
|
DescribeError(GetLastError()));
|
|
}
|
|
}
|
|
|
|
// we don't stop until we've confirmed they're all gone
|
|
count += subcount;
|
|
} while (!dryrun && subcount);
|
|
|
|
if (!silent && !count) {
|
|
fprintf(stderr, "%s: no processes found\n", prog);
|
|
}
|
|
|
|
return 0;
|
|
}
|