diff -ruN /usr/ports/sysutils/cpulimit/Makefile cpulimit/Makefile --- /usr/ports/sysutils/cpulimit/Makefile 2011-04-01 00:46:59.000000000 -0300 +++ cpulimit/Makefile 2011-08-12 21:31:43.000000000 -0300 @@ -2,20 +2,17 @@ # Date created: 30 Mar 2011 # Whom: Jesse Smith # -# $FreeBSD: ports/sysutils/cpulimit/Makefile,v 1.2 2011/04/01 03:46:59 danfe Exp $ +# $FreeBSD: $ # PORTNAME= cpulimit -PORTVERSION= 1.1 +PORTVERSION= 1.4 CATEGORIES= sysutils -MASTER_SITES= SF/${PORTNAME}/${PORTNAME}/${PORTNAME}/ +MASTER_SITES= SF/limitcpu/limitcpu/ MAINTAINER= jessefrgsmith@yahoo.ca COMMENT= A program to limit the CPU usage of a process -PLIST_FILES= bin/${PORTNAME} - -do-install: - ${INSTALL_PROGRAM} ${WRKSRC}/${PORTNAME} ${PREFIX}/bin +PLIST_FILES= bin/${PORTNAME} man/man1/${PORTNAME}.1.gz .include diff -ruN /usr/ports/sysutils/cpulimit/distinfo cpulimit/distinfo --- /usr/ports/sysutils/cpulimit/distinfo 2011-03-31 12:01:45.000000000 -0300 +++ cpulimit/distinfo 1969-12-31 20:00:00.000000000 -0400 @@ -1,2 +0,0 @@ -SHA256 (cpulimit-1.1.tar.gz) = ee734e82692dc496a083c003340b326bd779567f5de99fcae99e451606c85c00 -SIZE (cpulimit-1.1.tar.gz) = 5130 diff -ruN /usr/ports/sysutils/cpulimit/files/patch-Makefile cpulimit/files/patch-Makefile --- /usr/ports/sysutils/cpulimit/files/patch-Makefile 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/files/patch-Makefile 2011-08-12 21:33:40.000000000 -0300 @@ -0,0 +1,23 @@ +--- ./Makefile.orig 2011-08-12 19:26:33.000000000 -0300 ++++ ./Makefile 2011-08-12 21:33:31.000000000 -0300 +@@ -1,16 +1,16 @@ +-PREFIX?=/usr ++PREFIX?=/usr/local + CFLAGS?=-Wall -O2 + + all: cpulimit + + cpulimit: cpulimit.c +- gcc -o cpulimit cpulimit.c -lrt $(CFLAGS) ++ gcc -o cpulimit cpulimit.c -lrt $(CFLAGS) -lkvm + + install: cpulimit + mkdir -p ${PREFIX}/bin +- mkdir -p ${PREFIX}/share/man/man1 ++ mkdir -p ${PREFIX}/man/man1 + cp cpulimit ${PREFIX}/bin +- cp cpulimit.1.gz ${PREFIX}/share/man/man1 ++ cp cpulimit.1.gz ${PREFIX}/man/man1 + + deinstall: + rm -f ${PREFIX}/bin/cpulimit diff -ruN /usr/ports/sysutils/cpulimit/files/patch-cpulimit.c cpulimit/files/patch-cpulimit.c --- /usr/ports/sysutils/cpulimit/files/patch-cpulimit.c 2011-03-31 12:01:45.000000000 -0300 +++ cpulimit/files/patch-cpulimit.c 2011-08-12 21:33:40.000000000 -0300 @@ -1,8 +1,8 @@ ---- cpulimit.c.orig 2010-08-20 19:35:15.000000000 -0300 -+++ cpulimit.c 2010-08-21 14:17:52.000000000 -0300 -@@ -43,6 +43,15 @@ - #include - #include +--- ./cpulimit.c.orig 2011-08-12 19:35:36.000000000 -0300 ++++ ./cpulimit.c 2011-08-12 21:31:58.000000000 -0300 +@@ -35,6 +35,15 @@ + #include // for compatibility + +#include +#include @@ -16,7 +16,7 @@ //kernel time resolution (inverse of one jiffy interval) in Hertz //i don't know how to detect it, then define to the default (not very clean!) #define HZ 100 -@@ -235,6 +244,31 @@ +@@ -245,6 +254,31 @@ } //get jiffies count from /proc filesystem @@ -48,9 +48,9 @@ int getjiffies(int pid) { static char stat[20]; static char buffer[1024]; -@@ -255,6 +289,8 @@ - int ktime=atoi(p+1); - return utime+ktime; +@@ -271,6 +305,8 @@ + // could not read info + return -1; } +*/ + diff -ruN /usr/ports/sysutils/cpulimit/files/patch-makefile cpulimit/files/patch-makefile --- /usr/ports/sysutils/cpulimit/files/patch-makefile 2011-03-31 12:01:45.000000000 -0300 +++ cpulimit/files/patch-makefile 1969-12-31 20:00:00.000000000 -0400 @@ -1,19 +0,0 @@ ---- Makefile.old 2005-06-24 07:53:43.000000000 -0300 -+++ Makefile 2010-08-21 15:10:45.000000000 -0300 -@@ -1,7 +1,15 @@ -+PREFIX=/usr/local -+ - all:: cpulimit - - cpulimit: cpulimit.c -- gcc -o cpulimit cpulimit.c -lrt -Wall -O2 -+ gcc -o cpulimit cpulimit.c -lrt -Wall -O2 -lkvm -+ -+install: cpulimit -+ cp cpulimit ${PREFIX}/bin -+ -+deinstall: -+ rm -f ${PREFIX}/bin/cpulimit - - clean: - rm -f *~ cpulimit diff -ruN /usr/ports/sysutils/cpulimit/work/.PLIST.flattened cpulimit/work/.PLIST.flattened --- /usr/ports/sysutils/cpulimit/work/.PLIST.flattened 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/.PLIST.flattened 2011-08-12 21:34:26.000000000 -0300 @@ -0,0 +1,2 @@ +/usr/local/bin/cpulimit +/usr/local/man/man1/cpulimit.1.gz diff -ruN /usr/ports/sysutils/cpulimit/work/.PLIST.mktmp cpulimit/work/.PLIST.mktmp --- /usr/ports/sysutils/cpulimit/work/.PLIST.mktmp 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/.PLIST.mktmp 2011-08-12 21:34:23.000000000 -0300 @@ -0,0 +1,2 @@ +bin/cpulimit +man/man1/cpulimit.1.gz diff -ruN /usr/ports/sysutils/cpulimit/work/.PLIST.objdump cpulimit/work/.PLIST.objdump --- /usr/ports/sysutils/cpulimit/work/.PLIST.objdump 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/.PLIST.objdump 2011-08-12 21:34:26.000000000 -0300 @@ -0,0 +1,41 @@ + +/usr/local/bin/cpulimit: file format elf32-i386-freebsd + +DYNAMIC RELOCATION RECORDS +OFFSET TYPE VALUE +0804b580 R_386_COPY optarg +0804b584 R_386_COPY __stdoutp +0804b588 R_386_COPY __stderrp +0804b500 R_386_JUMP_SLOT kvm_open +0804b504 R_386_JUMP_SLOT puts +0804b508 R_386_JUMP_SLOT fprintf +0804b50c R_386_JUMP_SLOT atoi +0804b510 R_386_JUMP_SLOT setpriority +0804b514 R_386_JUMP_SLOT kill +0804b518 R_386_JUMP_SLOT strncmp +0804b51c R_386_JUMP_SLOT nanosleep +0804b520 R_386_JUMP_SLOT fork +0804b524 R_386_JUMP_SLOT clock_gettime +0804b528 R_386_JUMP_SLOT _init_tls +0804b52c R_386_JUMP_SLOT abort +0804b530 R_386_JUMP_SLOT opendir +0804b534 R_386_JUMP_SLOT printf +0804b538 R_386_JUMP_SLOT getopt_long +0804b53c R_386_JUMP_SLOT memrchr +0804b540 R_386_JUMP_SLOT signal +0804b544 R_386_JUMP_SLOT fwrite +0804b548 R_386_JUMP_SLOT setsid +0804b54c R_386_JUMP_SLOT exit +0804b550 R_386_JUMP_SLOT readdir +0804b554 R_386_JUMP_SLOT kvm_close +0804b558 R_386_JUMP_SLOT atexit +0804b55c R_386_JUMP_SLOT getpid +0804b560 R_386_JUMP_SLOT strlen +0804b564 R_386_JUMP_SLOT closedir +0804b568 R_386_JUMP_SLOT sleep +0804b56c R_386_JUMP_SLOT sprintf +0804b570 R_386_JUMP_SLOT readlink +0804b574 R_386_JUMP_SLOT perror +0804b578 R_386_JUMP_SLOT kvm_getprocs + + diff -ruN /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/CHANGELOG cpulimit/work/cpulimit-1.4/CHANGELOG --- /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/CHANGELOG 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/cpulimit-1.4/CHANGELOG 2011-08-12 19:39:42.000000000 -0300 @@ -0,0 +1,56 @@ +======== Changes in 1.4 ============ + +* We can now accept limits of 100% or higher. Useful for multi-core + systems. + +* Perform sanity check when getting jiffies. Should prevent memory + errors if we cannot open proc data. + +* Added copyright to README. + + +========== Changes in 1.3 ============ + +* Updated license information in cpulimit.c and README file + +* The -b flag is now shown under options instead of targets + in the help text. + +* Include man page from Debian with updates. + + +========= Changes in 1.2 =========== + +* Applied Debian patch for checking to see if and how much we can + adjust our own process priority. + +* Added LICENSE file so there wouldn't be any confusion about + what license CPUlimit uses. + +* Applied Debian's patch for long options to avoid segfault. + +* Applied Debian's Makefile patch. + +* Added Debian patch to avoid opendir leaks. + +* Added -b command line parameter to make CPUlimit + run in the background, returning control the the + user's terminal. + +* When cpulimit is launched with one PID to track + once that process no longer exists, CPUlimit + will exit. Same behaviour as though the lazy + flag was set. + +* Ported CPUlimit to FreeBSD + + +======= cpulimit-1.1 released ============ + +* Fixed a segmentation fault if controlled process exited in particular circumstances +* Better CPU usage estimate +* Fixed a <0 %CPU usage reporting in rare cases +* Replaced MAX_PATH_SIZE with PATH_MAX already defined in +* Command line arguments now available +* Now is possible to specify target process by pid + diff -ruN /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/LICENSE cpulimit/work/cpulimit-1.4/LICENSE --- /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/LICENSE 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/cpulimit-1.4/LICENSE 2011-04-17 22:21:47.000000000 -0300 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff -ruN /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/Makefile cpulimit/work/cpulimit-1.4/Makefile --- /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/Makefile 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/cpulimit-1.4/Makefile 2011-08-12 21:34:22.000000000 -0300 @@ -0,0 +1,20 @@ +PREFIX?=/usr/local +CFLAGS?=-Wall -O2 + +all: cpulimit + +cpulimit: cpulimit.c + gcc -o cpulimit cpulimit.c -lrt $(CFLAGS) -lkvm + +install: cpulimit + mkdir -p ${PREFIX}/bin + mkdir -p ${PREFIX}/man/man1 + cp cpulimit ${PREFIX}/bin + cp cpulimit.1.gz ${PREFIX}/man/man1 + +deinstall: + rm -f ${PREFIX}/bin/cpulimit + rm -f ${PREFIX}/share/man/man1/cpulimit.1.gz + +clean: + rm -f *~ cpulimit diff -ruN /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/Makefile.orig cpulimit/work/cpulimit-1.4/Makefile.orig --- /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/Makefile.orig 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/cpulimit-1.4/Makefile.orig 2011-08-12 19:26:33.000000000 -0300 @@ -0,0 +1,20 @@ +PREFIX?=/usr +CFLAGS?=-Wall -O2 + +all: cpulimit + +cpulimit: cpulimit.c + gcc -o cpulimit cpulimit.c -lrt $(CFLAGS) + +install: cpulimit + mkdir -p ${PREFIX}/bin + mkdir -p ${PREFIX}/share/man/man1 + cp cpulimit ${PREFIX}/bin + cp cpulimit.1.gz ${PREFIX}/share/man/man1 + +deinstall: + rm -f ${PREFIX}/bin/cpulimit + rm -f ${PREFIX}/share/man/man1/cpulimit.1.gz + +clean: + rm -f *~ cpulimit diff -ruN /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/README cpulimit/work/cpulimit-1.4/README --- /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/README 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/cpulimit-1.4/README 2011-08-12 19:31:02.000000000 -0300 @@ -0,0 +1,105 @@ +README for LimitCPU +========================== + +LimitCPU is a program to throttle the CPU cycles used by other applications. +LimitCPU will monitor a process and make sure its CPU usage stays at or +below a given percentage. This can be used to make sure your system +has plenty of CPU cycles available for other tasks. It can also be used +to keep laptops cool in the face of CPU-hungry processes and for limiting +virtual machines. + +LimitCPU is the direct child of CPUlimit, a creation of Angelo Marletta, +which can be found at http://cpulimit.sourceforge.net. + + + + +Copying, License and Distribution +=================================== + +LimitCPU is licensed under the GNU General Public License (version 2). +A copy of the license should be included with this program in a +file named LICENSE. + +Copyright 2005, Angelo Marletta +Copyright 2011, Jesse Smith + + + + + +Where to get LimitCPU +========================== + +The LimitCPU program can be aquired from http://limitcpu.sourceforge.net + + + + +How to compile and install +=========================== + +Once you have downloaded a copy of LimitCPU building should be fairly +straight forward. First we unpack the source code + +tar zxf cpulimit-1.4.tar.gz + +Then we run the makefile. + +cd cpulimit-1.4 +make + +This should produce the executable file "cpulimit". If you would like +to install the program to make it available system-wide, run + +make install + + +Later should you wish to remove the program from your system, run +the following command from the limitcpu directory + +make deinstall + + + +Common usage +========================== + +The LimitCPU program is generally used to throttle the CPU usage of +one process. This can be done with the following command where +12345 is the process ID number of a running program and 25 is the +maximum percentage of the CPU we are willing to give that program + +cpulimit -p 12345 -l 25 + +The above example will cause LimitCPU to keep an eye on the process +with ID number 12345 until the program exits. Should we wish to +run LimitCPU in the background we can use + +cpulimit -p 12345 -l 25 -b + +We can also limit running processes based on their name instead of +their process ID, as in this example: + +cpulimit --exe /usr/bin/bigexe --limit 50 + +The above example will keep an eye on "bigexe" and, if the application +quits and another program called "bigexe" is run, LimitCPU will +monitor the new process too. Should we wish to only track the first +program and then exit, we can use + +cpulimit --exec /usr/bin/bigexe --limit 50 -z + +The "-z" flag tells LimitCPU to stop running once its target is +no longer running itself. + + + + +Bugs and Feedback +============================= + +Should you have comments, questions, or bugs to report, please send +an e-mail to jessefrgsmith@yahoo.ca with the word "LimitCPU" in the +subject line. + Files /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/cpulimit and cpulimit/work/cpulimit-1.4/cpulimit differ Files /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/cpulimit.1.gz and cpulimit/work/cpulimit-1.4/cpulimit.1.gz differ diff -ruN /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/cpulimit.c cpulimit/work/cpulimit-1.4/cpulimit.c --- /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/cpulimit.c 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/cpulimit-1.4/cpulimit.c 2011-08-12 21:34:22.000000000 -0300 @@ -0,0 +1,655 @@ +/** + * This program is licensed under the GNU General Public License, + * version 2. A copy of the license can be found in the accompanying + * LICENSE file. + * + ********************************************************************** + * + * Simple program to limit the cpu usage of a process + * If you modify this code, send me a copy please + * + * Author: Angelo Marletta + * Date: 26/06/2005 + * Version: 1.1 + * + * Modifications and updates by: Jesse Smith + * Date: May 4, 2011 + * Version 1.2 + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for compatibility + + +#include +#include +#include +#include +#include +#include +#include + + +//kernel time resolution (inverse of one jiffy interval) in Hertz +//i don't know how to detect it, then define to the default (not very clean!) +#define HZ 100 + +//some useful macro +#define min(a,b) (ab?a:b) + +//pid of the controlled process +int pid=0; +//executable file name +char *program_name; +//verbose mode +int verbose=0; +//lazy mode +int lazy=0; +// is higher priority nice possible? +int nice_lim; + +//reverse byte search +void *memrchr(const void *s, int c, size_t n); + +//return ta-tb in microseconds (no overflow checks!) +inline long timediff(const struct timespec *ta,const struct timespec *tb) { + unsigned long us = (ta->tv_sec-tb->tv_sec)*1000000 + (ta->tv_nsec/1000 - tb->tv_nsec/1000); + return us; +} + +int waitforpid(int pid) { + //switch to low priority + // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) { + if ( (nice_lim < INT_MAX) && + (setpriority(PRIO_PROCESS, getpid(), 19) != 0) ) { + printf("Warning: cannot renice\n"); + } + + int i=0; + + while(1) { + + DIR *dip; + struct dirent *dit; + + //open a directory stream to /proc directory + if ((dip = opendir("/proc")) == NULL) { + perror("opendir"); + return -1; + } + + //read in from /proc and seek for process dirs + while ((dit = readdir(dip)) != NULL) { + //get pid + if (pid==atoi(dit->d_name)) { + //pid detected + if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) { + //process is ok! + if (closedir(dip) == -1) { + perror("closedir"); + return -1; + } + goto done; + } + else { + fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid); + } + } + } + + //close the dir stream and check for errors + if (closedir(dip) == -1) { + perror("closedir"); + return -1; + } + + //no suitable target found + if (i++==0) { + if (lazy) { + fprintf(stderr,"No process found\n"); + exit(2); + } + else { + printf("Warning: no target process found. Waiting for it...\n"); + } + } + + //sleep for a while + sleep(2); + } + +done: + printf("Process %d detected\n",pid); + //now set high priority, if possible + // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) { + if ( (nice_lim < INT_MAX) && + (setpriority(PRIO_PROCESS, getpid(), nice_lim) != 0) ) { + printf("Warning: cannot renice.\nTo work better you should run this program as root.\n"); + } + return 0; + +} + +//this function periodically scans process list and looks for executable path names +//it should be executed in a low priority context, since precise timing does not matter +//if a process is found then its pid is returned +//process: the name of the wanted process, can be an absolute path name to the executable file +// or simply its name +//return: pid of the found process +int getpidof(const char *process) { + + //set low priority + // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) { + if ( (nice_lim < INT_MAX) && + (setpriority(PRIO_PROCESS, getpid(), 19) != 0) ) { + printf("Warning: cannot renice\n"); + } + + char exelink[20]; + char exepath[PATH_MAX+1]; + int pid=0; + int i=0; + + while(1) { + + DIR *dip; + struct dirent *dit; + + //open a directory stream to /proc directory + if ((dip = opendir("/proc")) == NULL) { + perror("opendir"); + return -1; + } + + //read in from /proc and seek for process dirs + while ((dit = readdir(dip)) != NULL) { + //get pid + pid=atoi(dit->d_name); + if (pid>0) { + sprintf(exelink,"/proc/%d/exe",pid); + int size=readlink(exelink,exepath,sizeof(exepath)); + if (size>0) { + int found=0; + if (process[0]=='/' && strncmp(exepath,process,size)==0 && size==strlen(process)) { + //process starts with / then it's an absolute path + found=1; + } + else { + //process is the name of the executable file + if (strncmp(exepath+size-strlen(process),process,strlen(process))==0) { + found=1; + } + } + if (found==1) { + if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) { + //process is ok! + if (closedir(dip) == -1) { + perror("closedir"); + return -1; + } + goto done; + } + else { + fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid); + } + } + } + } + } + + //close the dir stream and check for errors + if (closedir(dip) == -1) { + perror("closedir"); + return -1; + } + + //no suitable target found + if (i++==0) { + if (lazy) { + fprintf(stderr,"No process found\n"); + exit(2); + } + else { + printf("Warning: no target process found. Waiting for it...\n"); + } + } + + //sleep for a while + sleep(2); + } + +done: + printf("Process %d detected\n",pid); + //now set high priority, if possible + // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) { + if ( (nice_lim < INT_MAX) && + (setpriority(PRIO_PROCESS, getpid(), nice_lim) != 0) ) { + printf("Warning: cannot renice.\nTo work better you should run this program as root.\n"); + } + return pid; + +} + +//SIGINT and SIGTERM signal handler +void quit(int sig) { + //let the process continue if it's stopped + kill(pid,SIGCONT); + printf("Exiting...\n"); + exit(0); +} + +//get jiffies count from /proc filesystem +int getjiffies(int pid) +{ + kvm_t *my_kernel = NULL; + struct kinfo_proc *process_data = NULL; + int processes; + int my_jiffies = -1; + + my_kernel = kvm_open(0, 0, 0, O_RDONLY, "kvm_open"); + if (! my_kernel) + { + printf("Error opening kernel vm. You should be running as root.\n"); + return -1; + } + + process_data = kvm_getprocs(my_kernel, KERN_PROC_PID, pid, &processes); + if ( (process_data) && (processes >= 1) ) + my_jiffies = process_data->ki_runtime; + + kvm_close(my_kernel); + if (my_jiffies >= 0) + my_jiffies /= 1000; + return my_jiffies; +} + +/* +int getjiffies(int pid) { + static char stat[20]; + static char buffer[1024]; + char *p; + sprintf(stat,"/proc/%d/stat",pid); + FILE *f=fopen(stat,"r"); + if (f==NULL) return -1; + p = fgets(buffer,sizeof(buffer),f); + fclose(f); + // char *p=buffer; + if (p) + { + p=memchr(p+1,')',sizeof(buffer)-(p-buffer)); + int sp=12; + while (sp--) + p=memchr(p+1,' ',sizeof(buffer)-(p-buffer)); + //user mode jiffies + int utime=atoi(p+1); + p=memchr(p+1,' ',sizeof(buffer)-(p-buffer)); + //kernel mode jiffies + int ktime=atoi(p+1); + return utime+ktime; + } + // could not read info + return -1; +} +*/ + + +//process instant photo +struct process_screenshot { + struct timespec when; //timestamp + int jiffies; //jiffies count of the process + int cputime; //microseconds of work from previous screenshot to current +}; + +//extracted process statistics +struct cpu_usage { + float pcpu; + float workingrate; +}; + +//this function is an autonomous dynamic system +//it works with static variables (state variables of the system), that keep memory of recent past +//its aim is to estimate the cpu usage of the process +//to work properly it should be called in a fixed periodic way +//perhaps i will put it in a separate thread... +int compute_cpu_usage(int pid,int last_working_quantum,struct cpu_usage *pusage) { + #define MEM_ORDER 10 + //circular buffer containing last MEM_ORDER process screenshots + static struct process_screenshot ps[MEM_ORDER]; + //the last screenshot recorded in the buffer + static int front=-1; + //the oldest screenshot recorded in the buffer + static int tail=0; + + if (pusage==NULL) { + //reinit static variables + front=-1; + tail=0; + return 0; + } + + //let's advance front index and save the screenshot + front=(front+1)%MEM_ORDER; + int j=getjiffies(pid); + if (j>=0) ps[front].jiffies=j; + else return -1; //error: pid does not exist + clock_gettime(CLOCK_REALTIME,&(ps[front].when)); + ps[front].cputime=last_working_quantum; + + //buffer actual size is: (front-tail+MEM_ORDER)%MEM_ORDER+1 + int size=(front-tail+MEM_ORDER)%MEM_ORDER+1; + + if (size==1) { + //not enough samples taken (it's the first one!), return -1 + pusage->pcpu=-1; + pusage->workingrate=1; + return 0; + } + else { + //now we can calculate cpu usage, interval dt and dtwork are expressed in microseconds + long dt=timediff(&(ps[front].when),&(ps[tail].when)); + long dtwork=0; + int i=(tail+1)%MEM_ORDER; + int max=(front+1)%MEM_ORDER; + do { + dtwork+=ps[i].cputime; + i=(i+1)%MEM_ORDER; + } while (i!=max); + int used=ps[front].jiffies-ps[tail].jiffies; + float usage=(used*1000000.0/HZ)/dtwork; + pusage->workingrate=1.0*dtwork/dt; + pusage->pcpu=usage*pusage->workingrate; + if (size==MEM_ORDER) + tail=(tail+1)%MEM_ORDER; + return 0; + } + #undef MEM_ORDER +} + +void print_caption() { + printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n"); +} + +void print_usage(FILE *stream,int exit_code) { + fprintf(stream, "Usage: %s TARGET [OPTIONS...]\n",program_name); + fprintf(stream, " TARGET must be exactly one of these:\n"); + fprintf(stream, " -p, --pid=N pid of the process\n"); + fprintf(stream, " -e, --exe=FILE name of the executable program file\n"); + fprintf(stream, " -P, --path=PATH absolute path name of the executable program file\n"); + fprintf(stream, " OPTIONS\n"); + fprintf(stream, " -b --background run in background\n"); + fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to 100 (mandatory)\n"); + fprintf(stream, " -v, --verbose show control statistics\n"); + fprintf(stream, " -z, --lazy exit if there is no suitable target process, or if it dies\n"); + fprintf(stream, " -h, --help display this help and exit\n"); + exit(exit_code); +} + +int main(int argc, char **argv) { + + //get program name + char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0])); + program_name = p==NULL?argv[0]:(p+1); + int run_in_background = 0; + //parse arguments + int next_option; + /* A string listing valid short options letters. */ + const char* short_options="p:e:P:l:bvzh"; + /* An array describing valid long options. */ + const struct option long_options[] = { + { "pid", required_argument, NULL, 'p' }, + { "exe", required_argument, NULL, 'e' }, + { "path", required_argument, NULL, 'P' }, + { "limit", required_argument, NULL, 'l' }, + { "background", no_argument, NULL, 'b' }, + { "verbose", no_argument, NULL, 'v' }, + { "lazy", no_argument, NULL, 'z' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + //argument variables + const char *exe=NULL; + const char *path=NULL; + int perclimit=0; + int pid_ok=0; + int process_ok=0; + int limit_ok=0; + struct rlimit maxlimit; + + do { + next_option = getopt_long (argc, argv, short_options,long_options, NULL); + switch(next_option) { + case 'b': + run_in_background = 1; + break; + case 'p': + pid=atoi(optarg); + pid_ok=1; + lazy = 1; + break; + case 'e': + exe=optarg; + process_ok=1; + break; + case 'P': + path=optarg; + process_ok=1; + break; + case 'l': + perclimit=atoi(optarg); + limit_ok=1; + break; + case 'v': + verbose=1; + break; + case 'z': + lazy=1; + break; + case 'h': + print_usage (stdout, 1); + break; + case '?': + print_usage (stderr, 1); + break; + case -1: + break; + default: + abort(); + } + } while(next_option != -1); + + if (!process_ok && !pid_ok) { + fprintf(stderr,"Error: You must specify a target process\n"); + print_usage (stderr, 1); + exit(1); + } + if ((exe!=NULL && path!=NULL) || (pid_ok && (exe!=NULL || path!=NULL))) { + fprintf(stderr,"Error: You must specify exactly one target process\n"); + print_usage (stderr, 1); + exit(1); + } + if (!limit_ok) { + fprintf(stderr,"Error: You must specify a cpu limit\n"); + print_usage (stderr, 1); + exit(1); + } + float limit=perclimit/100.0; + if (limit <= 0.00) // || limit >1) { + { + fprintf(stderr,"Error: limit must be greater than 0\n"); + print_usage (stderr, 1); + exit(1); + } + + // check to see if we should fork + if (run_in_background) + { + pid_t process_id; + process_id = fork(); + if (! process_id) + exit(0); + else + { + setsid(); + process_id = fork(); + if (process_id) + exit(0); + } + } + + //parameters are all ok! + signal(SIGINT,quit); + signal(SIGTERM,quit); + + if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) { + //if that failed, check if we have a limit + // by how much we can raise the priority +#ifdef RLIMIT_NICE +//check if non-root can even make changes +// (ifdef because it's only available in linux >= 2.6.13) + nice_lim=getpriority(PRIO_PROCESS,getpid()); + getrlimit(RLIMIT_NICE, &maxlimit); + +//if we can do better then current + if( (20 - (signed)maxlimit.rlim_cur) < nice_lim && + setpriority(PRIO_PROCESS,getpid(), + 20 - (signed)maxlimit.rlim_cur)==0 //and it actually works + ) { + + //if we can do better, but not by much, warn about it + if( (nice_lim - (20 - (signed)maxlimit.rlim_cur)) < 9) + { + printf("Warning, can only increase priority by %d.\n", nice_lim - (20 - (signed)maxlimit.rlim_cur)); + } + //our new limit + nice_lim = 20 - (signed)maxlimit.rlim_cur; + + } else +// otherwise don't try to change priority. +// The below will also run if it's not possible +// for non-root to change priority +#endif + { + printf("Warning: cannot renice.\nTo work better you should run this program as root, or adjust RLIMIT_NICE.\nFor example in /etc/security/limits.conf add a line with: * - nice -10\n\n"); + nice_lim=INT_MAX; + } + } else { + nice_lim=-20; + } + //don't bother putting setpriority back down, + // since getpidof and waitforpid twiddle it anyway + + + + //time quantum in microseconds. it's splitted in a working period and a sleeping one + int period=100000; + struct timespec twork,tsleep; //working and sleeping intervals + memset(&twork,0,sizeof(struct timespec)); + memset(&tsleep,0,sizeof(struct timespec)); + +wait_for_process: + + //look for the target process..or wait for it + if (exe!=NULL) + pid=getpidof(exe); + else if (path!=NULL) + pid=getpidof(path); + else { + waitforpid(pid); + } + //process detected...let's play + + //init compute_cpu_usage internal stuff + compute_cpu_usage(0,0,NULL); + //main loop counter + int i=0; + + struct timespec startwork,endwork; + long workingtime=0; //last working time in microseconds + + if (verbose) print_caption(); + + float pcpu_avg=0; + + //here we should already have high priority, for time precision + while(1) { + + //estimate how much the controlled process is using the cpu in its working interval + struct cpu_usage cu; + if (compute_cpu_usage(pid,workingtime,&cu)==-1) { + fprintf(stderr,"Process %d dead!\n",pid); + if (lazy) exit(2); + //wait until our process appears + goto wait_for_process; + } + + //cpu actual usage of process (range 0-1) + float pcpu=cu.pcpu; + //rate at which we are keeping active the process (range 0-1) + float workingrate=cu.workingrate; + + //adjust work and sleep time slices + if (pcpu>0) { + twork.tv_nsec=min(period*limit*1000/pcpu*workingrate,period*1000); + } + else if (pcpu==0) { + twork.tv_nsec=period*1000; + } + else if (pcpu==-1) { + //not yet a valid idea of cpu usage + pcpu=limit; + workingrate=limit; + twork.tv_nsec=min(period*limit*1000,period*1000); + } + tsleep.tv_nsec=period*1000-twork.tv_nsec; + + //update average usage + pcpu_avg=(pcpu_avg*i+pcpu)/(i+1); + + if (verbose && i%10==0 && i>0) { + printf("%0.2f%%\t%6ld us\t%6ld us\t%0.2f%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100); + } + + if (limit<1 && limit>0) { + //resume process + if (kill(pid,SIGCONT)!=0) { + fprintf(stderr,"Process %d dead!\n",pid); + if (lazy) exit(2); + //wait until our process appears + goto wait_for_process; + } + } + + clock_gettime(CLOCK_REALTIME,&startwork); + nanosleep(&twork,NULL); //now process is working + clock_gettime(CLOCK_REALTIME,&endwork); + workingtime=timediff(&endwork,&startwork); + + if (limit<1) { + //stop process, it has worked enough + if (kill(pid,SIGSTOP)!=0) { + fprintf(stderr,"Process %d dead!\n",pid); + if (lazy) exit(2); + //wait until our process appears + goto wait_for_process; + } + nanosleep(&tsleep,NULL); //now process is sleeping + } + i++; + } + +} diff -ruN /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/cpulimit.c.orig cpulimit/work/cpulimit-1.4/cpulimit.c.orig --- /usr/ports/sysutils/cpulimit/work/cpulimit-1.4/cpulimit.c.orig 1969-12-31 20:00:00.000000000 -0400 +++ cpulimit/work/cpulimit-1.4/cpulimit.c.orig 2011-08-12 19:35:36.000000000 -0300 @@ -0,0 +1,619 @@ +/** + * This program is licensed under the GNU General Public License, + * version 2. A copy of the license can be found in the accompanying + * LICENSE file. + * + ********************************************************************** + * + * Simple program to limit the cpu usage of a process + * If you modify this code, send me a copy please + * + * Author: Angelo Marletta + * Date: 26/06/2005 + * Version: 1.1 + * + * Modifications and updates by: Jesse Smith + * Date: May 4, 2011 + * Version 1.2 + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for compatibility + + +//kernel time resolution (inverse of one jiffy interval) in Hertz +//i don't know how to detect it, then define to the default (not very clean!) +#define HZ 100 + +//some useful macro +#define min(a,b) (ab?a:b) + +//pid of the controlled process +int pid=0; +//executable file name +char *program_name; +//verbose mode +int verbose=0; +//lazy mode +int lazy=0; +// is higher priority nice possible? +int nice_lim; + +//reverse byte search +void *memrchr(const void *s, int c, size_t n); + +//return ta-tb in microseconds (no overflow checks!) +inline long timediff(const struct timespec *ta,const struct timespec *tb) { + unsigned long us = (ta->tv_sec-tb->tv_sec)*1000000 + (ta->tv_nsec/1000 - tb->tv_nsec/1000); + return us; +} + +int waitforpid(int pid) { + //switch to low priority + // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) { + if ( (nice_lim < INT_MAX) && + (setpriority(PRIO_PROCESS, getpid(), 19) != 0) ) { + printf("Warning: cannot renice\n"); + } + + int i=0; + + while(1) { + + DIR *dip; + struct dirent *dit; + + //open a directory stream to /proc directory + if ((dip = opendir("/proc")) == NULL) { + perror("opendir"); + return -1; + } + + //read in from /proc and seek for process dirs + while ((dit = readdir(dip)) != NULL) { + //get pid + if (pid==atoi(dit->d_name)) { + //pid detected + if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) { + //process is ok! + if (closedir(dip) == -1) { + perror("closedir"); + return -1; + } + goto done; + } + else { + fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid); + } + } + } + + //close the dir stream and check for errors + if (closedir(dip) == -1) { + perror("closedir"); + return -1; + } + + //no suitable target found + if (i++==0) { + if (lazy) { + fprintf(stderr,"No process found\n"); + exit(2); + } + else { + printf("Warning: no target process found. Waiting for it...\n"); + } + } + + //sleep for a while + sleep(2); + } + +done: + printf("Process %d detected\n",pid); + //now set high priority, if possible + // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) { + if ( (nice_lim < INT_MAX) && + (setpriority(PRIO_PROCESS, getpid(), nice_lim) != 0) ) { + printf("Warning: cannot renice.\nTo work better you should run this program as root.\n"); + } + return 0; + +} + +//this function periodically scans process list and looks for executable path names +//it should be executed in a low priority context, since precise timing does not matter +//if a process is found then its pid is returned +//process: the name of the wanted process, can be an absolute path name to the executable file +// or simply its name +//return: pid of the found process +int getpidof(const char *process) { + + //set low priority + // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) { + if ( (nice_lim < INT_MAX) && + (setpriority(PRIO_PROCESS, getpid(), 19) != 0) ) { + printf("Warning: cannot renice\n"); + } + + char exelink[20]; + char exepath[PATH_MAX+1]; + int pid=0; + int i=0; + + while(1) { + + DIR *dip; + struct dirent *dit; + + //open a directory stream to /proc directory + if ((dip = opendir("/proc")) == NULL) { + perror("opendir"); + return -1; + } + + //read in from /proc and seek for process dirs + while ((dit = readdir(dip)) != NULL) { + //get pid + pid=atoi(dit->d_name); + if (pid>0) { + sprintf(exelink,"/proc/%d/exe",pid); + int size=readlink(exelink,exepath,sizeof(exepath)); + if (size>0) { + int found=0; + if (process[0]=='/' && strncmp(exepath,process,size)==0 && size==strlen(process)) { + //process starts with / then it's an absolute path + found=1; + } + else { + //process is the name of the executable file + if (strncmp(exepath+size-strlen(process),process,strlen(process))==0) { + found=1; + } + } + if (found==1) { + if (kill(pid,SIGSTOP)==0 && kill(pid,SIGCONT)==0) { + //process is ok! + if (closedir(dip) == -1) { + perror("closedir"); + return -1; + } + goto done; + } + else { + fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid); + } + } + } + } + } + + //close the dir stream and check for errors + if (closedir(dip) == -1) { + perror("closedir"); + return -1; + } + + //no suitable target found + if (i++==0) { + if (lazy) { + fprintf(stderr,"No process found\n"); + exit(2); + } + else { + printf("Warning: no target process found. Waiting for it...\n"); + } + } + + //sleep for a while + sleep(2); + } + +done: + printf("Process %d detected\n",pid); + //now set high priority, if possible + // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) { + if ( (nice_lim < INT_MAX) && + (setpriority(PRIO_PROCESS, getpid(), nice_lim) != 0) ) { + printf("Warning: cannot renice.\nTo work better you should run this program as root.\n"); + } + return pid; + +} + +//SIGINT and SIGTERM signal handler +void quit(int sig) { + //let the process continue if it's stopped + kill(pid,SIGCONT); + printf("Exiting...\n"); + exit(0); +} + +//get jiffies count from /proc filesystem +int getjiffies(int pid) { + static char stat[20]; + static char buffer[1024]; + char *p; + sprintf(stat,"/proc/%d/stat",pid); + FILE *f=fopen(stat,"r"); + if (f==NULL) return -1; + p = fgets(buffer,sizeof(buffer),f); + fclose(f); + // char *p=buffer; + if (p) + { + p=memchr(p+1,')',sizeof(buffer)-(p-buffer)); + int sp=12; + while (sp--) + p=memchr(p+1,' ',sizeof(buffer)-(p-buffer)); + //user mode jiffies + int utime=atoi(p+1); + p=memchr(p+1,' ',sizeof(buffer)-(p-buffer)); + //kernel mode jiffies + int ktime=atoi(p+1); + return utime+ktime; + } + // could not read info + return -1; +} + +//process instant photo +struct process_screenshot { + struct timespec when; //timestamp + int jiffies; //jiffies count of the process + int cputime; //microseconds of work from previous screenshot to current +}; + +//extracted process statistics +struct cpu_usage { + float pcpu; + float workingrate; +}; + +//this function is an autonomous dynamic system +//it works with static variables (state variables of the system), that keep memory of recent past +//its aim is to estimate the cpu usage of the process +//to work properly it should be called in a fixed periodic way +//perhaps i will put it in a separate thread... +int compute_cpu_usage(int pid,int last_working_quantum,struct cpu_usage *pusage) { + #define MEM_ORDER 10 + //circular buffer containing last MEM_ORDER process screenshots + static struct process_screenshot ps[MEM_ORDER]; + //the last screenshot recorded in the buffer + static int front=-1; + //the oldest screenshot recorded in the buffer + static int tail=0; + + if (pusage==NULL) { + //reinit static variables + front=-1; + tail=0; + return 0; + } + + //let's advance front index and save the screenshot + front=(front+1)%MEM_ORDER; + int j=getjiffies(pid); + if (j>=0) ps[front].jiffies=j; + else return -1; //error: pid does not exist + clock_gettime(CLOCK_REALTIME,&(ps[front].when)); + ps[front].cputime=last_working_quantum; + + //buffer actual size is: (front-tail+MEM_ORDER)%MEM_ORDER+1 + int size=(front-tail+MEM_ORDER)%MEM_ORDER+1; + + if (size==1) { + //not enough samples taken (it's the first one!), return -1 + pusage->pcpu=-1; + pusage->workingrate=1; + return 0; + } + else { + //now we can calculate cpu usage, interval dt and dtwork are expressed in microseconds + long dt=timediff(&(ps[front].when),&(ps[tail].when)); + long dtwork=0; + int i=(tail+1)%MEM_ORDER; + int max=(front+1)%MEM_ORDER; + do { + dtwork+=ps[i].cputime; + i=(i+1)%MEM_ORDER; + } while (i!=max); + int used=ps[front].jiffies-ps[tail].jiffies; + float usage=(used*1000000.0/HZ)/dtwork; + pusage->workingrate=1.0*dtwork/dt; + pusage->pcpu=usage*pusage->workingrate; + if (size==MEM_ORDER) + tail=(tail+1)%MEM_ORDER; + return 0; + } + #undef MEM_ORDER +} + +void print_caption() { + printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n"); +} + +void print_usage(FILE *stream,int exit_code) { + fprintf(stream, "Usage: %s TARGET [OPTIONS...]\n",program_name); + fprintf(stream, " TARGET must be exactly one of these:\n"); + fprintf(stream, " -p, --pid=N pid of the process\n"); + fprintf(stream, " -e, --exe=FILE name of the executable program file\n"); + fprintf(stream, " -P, --path=PATH absolute path name of the executable program file\n"); + fprintf(stream, " OPTIONS\n"); + fprintf(stream, " -b --background run in background\n"); + fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to 100 (mandatory)\n"); + fprintf(stream, " -v, --verbose show control statistics\n"); + fprintf(stream, " -z, --lazy exit if there is no suitable target process, or if it dies\n"); + fprintf(stream, " -h, --help display this help and exit\n"); + exit(exit_code); +} + +int main(int argc, char **argv) { + + //get program name + char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0])); + program_name = p==NULL?argv[0]:(p+1); + int run_in_background = 0; + //parse arguments + int next_option; + /* A string listing valid short options letters. */ + const char* short_options="p:e:P:l:bvzh"; + /* An array describing valid long options. */ + const struct option long_options[] = { + { "pid", required_argument, NULL, 'p' }, + { "exe", required_argument, NULL, 'e' }, + { "path", required_argument, NULL, 'P' }, + { "limit", required_argument, NULL, 'l' }, + { "background", no_argument, NULL, 'b' }, + { "verbose", no_argument, NULL, 'v' }, + { "lazy", no_argument, NULL, 'z' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + //argument variables + const char *exe=NULL; + const char *path=NULL; + int perclimit=0; + int pid_ok=0; + int process_ok=0; + int limit_ok=0; + struct rlimit maxlimit; + + do { + next_option = getopt_long (argc, argv, short_options,long_options, NULL); + switch(next_option) { + case 'b': + run_in_background = 1; + break; + case 'p': + pid=atoi(optarg); + pid_ok=1; + lazy = 1; + break; + case 'e': + exe=optarg; + process_ok=1; + break; + case 'P': + path=optarg; + process_ok=1; + break; + case 'l': + perclimit=atoi(optarg); + limit_ok=1; + break; + case 'v': + verbose=1; + break; + case 'z': + lazy=1; + break; + case 'h': + print_usage (stdout, 1); + break; + case '?': + print_usage (stderr, 1); + break; + case -1: + break; + default: + abort(); + } + } while(next_option != -1); + + if (!process_ok && !pid_ok) { + fprintf(stderr,"Error: You must specify a target process\n"); + print_usage (stderr, 1); + exit(1); + } + if ((exe!=NULL && path!=NULL) || (pid_ok && (exe!=NULL || path!=NULL))) { + fprintf(stderr,"Error: You must specify exactly one target process\n"); + print_usage (stderr, 1); + exit(1); + } + if (!limit_ok) { + fprintf(stderr,"Error: You must specify a cpu limit\n"); + print_usage (stderr, 1); + exit(1); + } + float limit=perclimit/100.0; + if (limit <= 0.00) // || limit >1) { + { + fprintf(stderr,"Error: limit must be greater than 0\n"); + print_usage (stderr, 1); + exit(1); + } + + // check to see if we should fork + if (run_in_background) + { + pid_t process_id; + process_id = fork(); + if (! process_id) + exit(0); + else + { + setsid(); + process_id = fork(); + if (process_id) + exit(0); + } + } + + //parameters are all ok! + signal(SIGINT,quit); + signal(SIGTERM,quit); + + if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) { + //if that failed, check if we have a limit + // by how much we can raise the priority +#ifdef RLIMIT_NICE +//check if non-root can even make changes +// (ifdef because it's only available in linux >= 2.6.13) + nice_lim=getpriority(PRIO_PROCESS,getpid()); + getrlimit(RLIMIT_NICE, &maxlimit); + +//if we can do better then current + if( (20 - (signed)maxlimit.rlim_cur) < nice_lim && + setpriority(PRIO_PROCESS,getpid(), + 20 - (signed)maxlimit.rlim_cur)==0 //and it actually works + ) { + + //if we can do better, but not by much, warn about it + if( (nice_lim - (20 - (signed)maxlimit.rlim_cur)) < 9) + { + printf("Warning, can only increase priority by %d.\n", nice_lim - (20 - (signed)maxlimit.rlim_cur)); + } + //our new limit + nice_lim = 20 - (signed)maxlimit.rlim_cur; + + } else +// otherwise don't try to change priority. +// The below will also run if it's not possible +// for non-root to change priority +#endif + { + printf("Warning: cannot renice.\nTo work better you should run this program as root, or adjust RLIMIT_NICE.\nFor example in /etc/security/limits.conf add a line with: * - nice -10\n\n"); + nice_lim=INT_MAX; + } + } else { + nice_lim=-20; + } + //don't bother putting setpriority back down, + // since getpidof and waitforpid twiddle it anyway + + + + //time quantum in microseconds. it's splitted in a working period and a sleeping one + int period=100000; + struct timespec twork,tsleep; //working and sleeping intervals + memset(&twork,0,sizeof(struct timespec)); + memset(&tsleep,0,sizeof(struct timespec)); + +wait_for_process: + + //look for the target process..or wait for it + if (exe!=NULL) + pid=getpidof(exe); + else if (path!=NULL) + pid=getpidof(path); + else { + waitforpid(pid); + } + //process detected...let's play + + //init compute_cpu_usage internal stuff + compute_cpu_usage(0,0,NULL); + //main loop counter + int i=0; + + struct timespec startwork,endwork; + long workingtime=0; //last working time in microseconds + + if (verbose) print_caption(); + + float pcpu_avg=0; + + //here we should already have high priority, for time precision + while(1) { + + //estimate how much the controlled process is using the cpu in its working interval + struct cpu_usage cu; + if (compute_cpu_usage(pid,workingtime,&cu)==-1) { + fprintf(stderr,"Process %d dead!\n",pid); + if (lazy) exit(2); + //wait until our process appears + goto wait_for_process; + } + + //cpu actual usage of process (range 0-1) + float pcpu=cu.pcpu; + //rate at which we are keeping active the process (range 0-1) + float workingrate=cu.workingrate; + + //adjust work and sleep time slices + if (pcpu>0) { + twork.tv_nsec=min(period*limit*1000/pcpu*workingrate,period*1000); + } + else if (pcpu==0) { + twork.tv_nsec=period*1000; + } + else if (pcpu==-1) { + //not yet a valid idea of cpu usage + pcpu=limit; + workingrate=limit; + twork.tv_nsec=min(period*limit*1000,period*1000); + } + tsleep.tv_nsec=period*1000-twork.tv_nsec; + + //update average usage + pcpu_avg=(pcpu_avg*i+pcpu)/(i+1); + + if (verbose && i%10==0 && i>0) { + printf("%0.2f%%\t%6ld us\t%6ld us\t%0.2f%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100); + } + + if (limit<1 && limit>0) { + //resume process + if (kill(pid,SIGCONT)!=0) { + fprintf(stderr,"Process %d dead!\n",pid); + if (lazy) exit(2); + //wait until our process appears + goto wait_for_process; + } + } + + clock_gettime(CLOCK_REALTIME,&startwork); + nanosleep(&twork,NULL); //now process is working + clock_gettime(CLOCK_REALTIME,&endwork); + workingtime=timediff(&endwork,&startwork); + + if (limit<1) { + //stop process, it has worked enough + if (kill(pid,SIGSTOP)!=0) { + fprintf(stderr,"Process %d dead!\n",pid); + if (lazy) exit(2); + //wait until our process appears + goto wait_for_process; + } + nanosleep(&tsleep,NULL); //now process is sleeping + } + i++; + } + +}