diff --git a/cpulimit/Makefile b/cpulimit/Makefile index 60fc18a..b9cc632 100644 --- a/cpulimit/Makefile +++ b/cpulimit/Makefile @@ -1,9 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=cpulimit -PKG_VERSION:=0.2 -PKG_RELEASE:=1 -#PKG_REV:=cabeb9947ccddd9a6e6ba14503e2a33063ac1b21 +PKG_VERSION:=0.3.2 +PKG_RELEASE:=$(AUTORELEASE) #PKG_SOURCE_PROTO:=git #PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 @@ -16,8 +15,8 @@ include $(INCLUDE_DIR)/package.mk define Package/cpulimit SECTION:=utils CATEGORY:=Utilities - TITLE:=cpulimit - URL:=https://github.com/opsengine/cpulimit.git + TITLE:=CPU usage limiter + URL:=https://github.com/denji/cpulimit endef define Build/Prepare diff --git a/cpulimit/src/Makefile b/cpulimit/src/Makefile index 54fde92..7b3107b 100644 --- a/cpulimit/src/Makefile +++ b/cpulimit/src/Makefile @@ -1,9 +1,10 @@ BINDIR ?= $(PREFIX)/bin BIN_TARGET = cpulimit -CC ?= gcc -CFLAGS ?= -O2 -Wall -g -D_GNU_SOURCE +CC ?= gcc +CFLAGS ?= -Os -Wall -g -D_GNU_SOURCE HELP2MAN := $(shell which help2man) LIBS = list.o process_iterator.o process_group.o +MANDIR ?= $(PREFIX)/share/man/man1 MAN_NAME := "CPU Utilization Limiter" MAN_SEC := 1 MAN_SRC := $(shell git remote get-url origin) @@ -20,14 +21,21 @@ endif all:: $(TARGETS) $(LIBS) -static: - $(MAKE) all CFLAGS="$(CFLAGS) --static" +static: $(TARGETS) $(LIBS) + $(MAKE) all CFLAGS="$(CFLAGS) --static" CC="musl-gcc" + touch static strip: $(BIN_TARGET) $(STRIP) $< + touch strip install: $(TARGETS) - $(SUDO) install $^ $(BINDIR) + $(SUDO) install -D $(BIN_TARGET) $(BINDIR)/$(BIN_TARGET) + $(SUDO) install -D $(MAN_TARGET) $(MANDIR)/$(MAN_TARGET) + touch install + +static_install: static strip install + touch static_install $(BIN_TARGET): cpulimit.c $(LIBS) $(CC) -o cpulimit cpulimit.c $(LIBS) $(CFLAGS) @@ -45,5 +53,5 @@ process_group.o: process_group.c process_group.h process_iterator.o list.o $(CC) -c process_group.c $(CFLAGS) clean: - rm -f *~ *.o $(TARGETS) + rm -f *~ *.o static strip install static_install $(TARGETS) diff --git a/cpulimit/src/cpulimit.c b/cpulimit/src/cpulimit.c index 21fff94..fb21659 100644 --- a/cpulimit/src/cpulimit.c +++ b/cpulimit/src/cpulimit.c @@ -23,7 +23,7 @@ * This is a simple program to limit the cpu usage of a process * If you modify this code, send me a copy please * - * Get the latest version at: http://github.com/opsengine/cpulimit + * Get the latest version at: http://github.com/denji/cpulimit * */ @@ -38,8 +38,11 @@ #include #include #include +/* This breaks under musl and isn't necessary under glibc #ifndef __sun__ +#include #endif +*/ #include #include #include @@ -67,14 +70,9 @@ #define MAX(a,b) (((a)>(b))?(a):(b)) #endif -//control time slot in microseconds -//each slot is splitted in a working slice and a sleeping slice -//TODO: make it adaptive, based on the actual system load -#define TIME_SLOT 100000 - #define MAX_PRIORITY -10 -#define VERSION "0.3" +#define VERSION "0.3.2" /* GLOBAL VARIABLES */ @@ -121,6 +119,7 @@ static void print_usage(FILE *stream, int exit_code) { fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); fprintf(stream, " OPTIONS\n"); fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100*NCPU); + fprintf(stream, " -c, --chunk-size=N enforce CPU limit in N microsecond chunks\n"); fprintf(stream, " -v, --verbose show control statistics\n"); fprintf(stream, " -V, --version show program version number\n"); fprintf(stream, " -z, --lazy exit if there is no target process, or if it dies\n"); @@ -193,7 +192,7 @@ int get_pid_max() { #endif } -void limit_process(pid_t pid, double limit, int include_children, float minimum_cpu_usage) { +void limit_process(pid_t pid, double limit, int include_children, float minimum_cpu_usage, int chunk_size) { //slice of the slot in which the process is allowed to run struct timespec twork; //slice of the slot in which the process is stopped @@ -258,13 +257,13 @@ void limit_process(pid_t pid, double limit, int include_children, float minimum_ //it's the 1st cycle, initialize workingrate pcpu = limit; workingrate = limit; - twork.tv_nsec = TIME_SLOT * limit * 1000; + twork.tv_nsec = chunk_size * limit * 1000; } else { //adjust workingrate workingrate = MIN(workingrate / pcpu * limit, 1); - twork.tv_nsec = TIME_SLOT * 1000 * workingrate; + twork.tv_nsec = chunk_size * 1000 * workingrate; } - tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec; + tsleep.tv_nsec = chunk_size * 1000 - twork.tv_nsec; if (verbose) { if (c%200==0) { @@ -339,6 +338,7 @@ int main(int argc, char **argv) { pid_t pid = 0; float minimum_cpu_usage=0; int include_children = 0; + int chunk_size = 100000; //get program name #ifdef __sun__ @@ -356,14 +356,15 @@ int main(int argc, char **argv) { int next_option; int option_index = 0; //A string listing valid short options letters - const char *short_options = "+p:e:l:vVzim:h"; + const char *short_options = "+p:e:l:c:vVzim:h"; //An array describing valid long options const struct option long_options[] = { { "pid", required_argument, NULL, 'p' }, { "exe", required_argument, NULL, 'e' }, { "limit", required_argument, NULL, 'l' }, + { "chunk-size", required_argument, NULL, 'c' }, { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, + { "version", no_argument, NULL, 'V' }, { "lazy", no_argument, NULL, 'z' }, { "include-children", no_argument, NULL, 'i' }, { "minimum-limited-cpu", no_argument, NULL, 'm' }, @@ -386,12 +387,15 @@ int main(int argc, char **argv) { perclimit = atoi(optarg); limit_ok = 1; break; + case 'c': + chunk_size = atoi(optarg); + break; case 'v': verbose = 1; break; - case 'V': - print_version(stdout, 0); - break; + case 'V': + print_version(stdout, 0); + break; case 'z': lazy = 1; break; @@ -435,6 +439,12 @@ int main(int argc, char **argv) { exit(1); } + if (0 > chunk_size || chunk_size > 1000000) { + fprintf(stderr,"Error: chunk size must be in the range 0-1000000\n"); + print_usage(stderr, 1); + exit(1); + } + int command_mode = optind < argc; if (exe_ok + pid_ok + command_mode == 0) { fprintf(stderr,"Error: You must specify one target process, either by name, pid, or command line\n"); @@ -513,7 +523,7 @@ int main(int argc, char **argv) { if (verbose) { printf("Limiting process %d\n",child); } - limit_process(child, limit, include_children, minimum_cpu_usage); + limit_process(child, limit, include_children, minimum_cpu_usage, chunk_size); exit(0); } } @@ -548,7 +558,7 @@ int main(int argc, char **argv) { } printf("Process %d found\n", pid); //control - limit_process(pid, limit, include_children, minimum_cpu_usage); + limit_process(pid, limit, include_children, minimum_cpu_usage, chunk_size); } if (lazy) { break; diff --git a/cpulimit/src/process_group.c b/cpulimit/src/process_group.c index 1a5dfd1..5e1efb6 100644 --- a/cpulimit/src/process_group.c +++ b/cpulimit/src/process_group.c @@ -32,7 +32,7 @@ #include #ifdef __sun__ -#include + #include #endif #include "process_iterator.h" diff --git a/cpulimit/src/process_iterator.c b/cpulimit/src/process_iterator.c index 53f68c4..61405bd 100644 --- a/cpulimit/src/process_iterator.c +++ b/cpulimit/src/process_iterator.c @@ -23,9 +23,10 @@ #include #include #ifdef __sun__ -#include + #include #elif !defined __APPLE__ -#include + #include + #include #endif #include #include "process_iterator.h" @@ -46,7 +47,7 @@ #elif defined __sun__ -#include "process_iterator_solaris.c" + #include "process_iterator_solaris.c" #else diff --git a/cpulimit/src/process_iterator_solaris.c b/cpulimit/src/process_iterator_solaris.c index cd63631..f49fca2 100644 --- a/cpulimit/src/process_iterator_solaris.c +++ b/cpulimit/src/process_iterator_solaris.c @@ -4,7 +4,7 @@ * Copyright (C) 2016 by Jim Mason * * Adapted from process_iterator_linux.c - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,97 +22,114 @@ */ int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - // open a directory stream to /proc directory - if (!(it->dip = opendir("/proc"))) { - perror("opendir"); - return -1; - } - it->filter = filter; - return 0; + // open a directory stream to /proc directory + if (!(it->dip = opendir("/proc"))) { + perror("opendir"); + return -1; + } + it->filter = filter; + return 0; } static int read_process_info(pid_t pid, struct process *p) { - psinfo_t psinfo; - char statfile[32]; + psinfo_t psinfo; + char statfile[32]; - p->pid = pid; - sprintf(statfile, "/proc/%ld/psinfo", (long)pid); - FILE *fd = fopen(statfile, "r"); - if (!fd) return -1; - if (!fread(&psinfo, sizeof(psinfo), 1, fd)) { - fclose(fd); - return -1; - } - fclose(fd); + p->pid = pid; + sprintf(statfile, "/proc/%ld/psinfo", (long)pid); + FILE *fd = fopen(statfile, "r"); + if (!fd) { + return -1; + } + if (!fread(&psinfo, sizeof(psinfo), 1, fd)) { + fclose(fd); + return -1; + } + fclose(fd); - p->ppid = psinfo.pr_ppid; - p->cputime = psinfo.pr_time.tv_sec * 1.0e03 + psinfo.pr_time.tv_nsec / 1.0e06; - p->starttime = psinfo.pr_start.tv_sec * 1.0e03 + psinfo.pr_start.tv_nsec / 1.0e06; - strcpy(p->command, psinfo.pr_psargs); + p->ppid = psinfo.pr_ppid; + p->cputime = psinfo.pr_time.tv_sec * 1.0e03 + psinfo.pr_time.tv_nsec / 1.0e06; + p->starttime = psinfo.pr_start.tv_sec * 1.0e03 + psinfo.pr_start.tv_nsec / 1.0e06; + strcpy(p->command, psinfo.pr_psargs); - return 0; + /** + * per proc(4): + * + * If the process is a zombie, pr_nlwp, pr_nzomb, and + * pr_lwp.pr_lwpid are zero and the other fields of pr_lwp + * are undefined + * + * In this case, we'll return -1 to the caller + */ + return psinfo.pr_nlwp?0:-1; } static pid_t getppid_of(pid_t pid) { - psinfo_t psinfo; - char statfile[32]; + psinfo_t psinfo; + char statfile[32]; - sprintf(statfile, "/proc/%ld/psinfo", (long)pid); - FILE *fd = fopen(statfile, "r"); - if (!fd) return -1; - if (!fread(&psinfo, sizeof(psinfo), 1, fd)) { - fclose(fd); - return -1; - } - fclose(fd); + sprintf(statfile, "/proc/%ld/psinfo", (long)pid); + FILE *fd = fopen(statfile, "r"); + if (!fd) { + return -1; + } + if (!fread(&psinfo, sizeof(psinfo), 1, fd)) { + fclose(fd); + return -1; + } + fclose(fd); - return psinfo.pr_ppid; + return psinfo.pr_ppid; } static int is_child_of(pid_t child_pid, pid_t parent_pid) { - int ppid = child_pid; - while(ppid > 1 && ppid != parent_pid) - ppid = getppid_of(ppid); - return ppid == parent_pid; + int ppid = child_pid; + while (ppid > 1 && ppid != parent_pid) { + ppid = getppid_of(ppid); + } + return ppid == parent_pid; } int get_next_process(struct process_iterator *it, struct process *p) { - if (!it->dip) { - // end of processes - return -1; - } + if (!it->dip) { + // end of processes + return -1; + } - if (it->filter->pid != 0 && !it->filter->include_children) { - int ret = read_process_info(it->filter->pid, p); - closedir(it->dip); - it->dip = NULL; - return ret; - } + if (it->filter->pid != 0 && !it->filter->include_children) { + int ret = read_process_info(it->filter->pid, p); + closedir(it->dip); + it->dip = NULL; + return ret; + } - // read in from /proc and seek for process dirs - struct dirent *dit; - while ((dit = readdir(it->dip))) { - p->pid = atoi(dit->d_name); - if (it->filter->pid != 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) continue; - read_process_info(p->pid, p); - break; - } + // read in from /proc and seek for process dirs + struct dirent *dit; + while ((dit = readdir(it->dip))) { + p->pid = atoi(dit->d_name); + if (it->filter->pid != 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) { + continue; + } + if (!read_process_info(p->pid, p)) { + break; + } + } - if (!dit) { - // end of processes - closedir(it->dip); - it->dip = NULL; - return -1; - } + if (!dit) { + // end of processes + closedir(it->dip); + it->dip = NULL; + return -1; + } - return 0; + return 0; } int close_process_iterator(struct process_iterator *it) { - if (it->dip && closedir(it->dip) == -1) { - perror("closedir"); - return 1; - } - it->dip = NULL; - return 0; + if (it->dip && closedir(it->dip) == -1) { + perror("closedir"); + return 1; + } + it->dip = NULL; + return 0; } diff --git a/luci-app-cpulimit/Makefile b/luci-app-cpulimit/Makefile index 5f51688..6df4cad 100644 --- a/luci-app-cpulimit/Makefile +++ b/luci-app-cpulimit/Makefile @@ -5,42 +5,11 @@ include $(TOPDIR)/rules.mk -PKG_NAME:=luci-app-cpulimit -PKG_VERSION=1.0 -PKG_RELEASE:=2 +LUCI_TITLE:=cpulimit configuration module +LUCI_DEPENDS:=+cpulimit -include $(INCLUDE_DIR)/package.mk -define Package/$(PKG_NAME) - SECTION:=luci - CATEGORY:=LuCI - SUBMENU:=3. Applications - DEPENDS:=+cpulimit - TITLE:=LuCI support for cpulimit - PKGARCH:=all -endef +include $(TOPDIR)/feeds/luci/luci.mk -define Build/Compile -endef -define Package/$(PKG_NAME)/postinst -#!/bin/sh -rm -f /tmp/luci-indexcache /tmp/luci-modulecache -endef - -define Package/$(PKG_NAME)/conffiles -/etc/config/cpulimit -endef - -define Package/$(PKG_NAME)/install - $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/usr/lib/lua/luci - $(CP) ./luasrc/* $(1)/usr/lib/lua/luci - $(INSTALL_CONF) ./root/etc/config/cpulimit $(1)/etc/config - $(INSTALL_BIN) ./root/etc/init.d/cpulimit $(1)/etc/init.d - $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) ./root/usr/bin/cpulimit.sh $(1)/usr/bin - $(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n - po2lmo ./po/zh-cn/cpulimit.po $(1)/usr/lib/lua/luci/i18n/cpulimit.zh-cn.lmo -endef - -$(eval $(call BuildPackage,$(PKG_NAME))) +# call BuildPackage - OpenWrt buildroot signature \ No newline at end of file diff --git a/luci-app-cpulimit/luasrc/model/cbi/cpulimit.lua b/luci-app-cpulimit/luasrc/model/cbi/cpulimit.lua index 40b3ba4..d3eaba0 100644 --- a/luci-app-cpulimit/luasrc/model/cbi/cpulimit.lua +++ b/luci-app-cpulimit/luasrc/model/cbi/cpulimit.lua @@ -18,7 +18,7 @@ end limit = s:option(Value, "limit", translate("limit")) limit.optional = false limit.rmempty = false -limit.default="50" +limit.default="30" limit:value("100","100%") limit:value("90","90%") limit:value("80","80%") diff --git a/luci-app-cpulimit/root/etc/uci-defaults/luci-app-cpulimit b/luci-app-cpulimit/root/etc/uci-defaults/luci-app-cpulimit new file mode 100644 index 0000000..7e66374 --- /dev/null +++ b/luci-app-cpulimit/root/etc/uci-defaults/luci-app-cpulimit @@ -0,0 +1,13 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@cpulimit[-1] + add ucitrack cpulimit + set ucitrack.@cpulimit[-1].init=cpulimit + commit ucitrack +EOF + +/etc/init.d/cpulimit enable + +rm -f /tmp/luci-indexcache +exit 0 diff --git a/luci-app-cpulimit/root/usr/bin/cpulimit.sh b/luci-app-cpulimit/root/usr/bin/cpulimit.sh index d9eafe5..e80b26a 100644 --- a/luci-app-cpulimit/root/usr/bin/cpulimit.sh +++ b/luci-app-cpulimit/root/usr/bin/cpulimit.sh @@ -20,6 +20,8 @@ killall -9 cpulimit # do # killall cpulimit>/dev/null 2>&1 # done + +# pgrep -f cpulimit | xargs kill -9 >/dev/null 2>&1 } case "$1" in