[svn-upgrade] new version cpulimit (1.3) upstream/1.3
authorgregor herrmann <gregoa@debian.org>
Sun, 8 May 2011 12:58:33 +0000 (12:58 -0000)
committergregor herrmann <gregoa@debian.org>
Sun, 8 May 2011 12:58:33 +0000 (12:58 -0000)
13 files changed:
CHANGELOG [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile
README
cpulimit.1.gz [new file with mode: 0644]
cpulimit.c
list.c [deleted file]
list.h [deleted file]
process.c [deleted file]
process.h [deleted file]
procutils.c [deleted file]
procutils.h [deleted file]
ptest.c [deleted file]

diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644 (file)
index 0000000..6c7a838
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,45 @@
+========== 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 <limits.h>
+* Command line arguments now available
+* Now is possible to specify target process by pid
+
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
+++ b/LICENSE
@@ -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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+  <signature of Ty Coon>, 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.
index 7bd54ec..39f62a1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,25 +1,18 @@
-CC=gcc
-CFLAGS=-Wall -D_GNU_SOURCE -O2
-TARGETS=cpulimit ptest
-LIBS=process.o procutils.o list.o
+PREFIX?=/usr
+CFLAGS?=-Wall -O2
 
-all::  $(TARGETS)
+all: cpulimit
 
-cpulimit:      cpulimit.c $(LIBS)
-       $(CC) -o cpulimit cpulimit.c $(LIBS) -lrt $(CFLAGS)
+cpulimit: cpulimit.c
+       gcc -o cpulimit cpulimit.c -lrt $(CFLAGS)
 
-process.o: process.c process.h
-       $(CC) -c process.c $(CFLAGS)
+install: cpulimit
+       cp cpulimit ${PREFIX}/bin
+       cp cpulimit.1.gz ${PREFIX}/share/man/man1
 
-procutils.o: procutils.c procutils.h
-       $(CC) -c procutils.c $(CFLAGS)
-
-list.o: list.c list.h
-       $(CC) -c list.c $(CFLAGS)
-
-ptest: ptest.c
-       $(CC) -o ptest ptest.c -lrt $(CFLAGS)
+deinstall:
+       rm -f ${PREFIX}/bin/cpulimit
+       rm -f ${PREFIX}/share/man/man1/cpulimit.1.gz
 
 clean:
-       rm -f *~ *.o $(TARGETS)
-
+       rm -f *~ cpulimit
diff --git a/README b/README
index 6ffa82c..447e099 100644 (file)
--- a/README
+++ b/README
-Cpulimit 1.2
+README for LimitCPU
+==========================
 
-=======
-About:
-=======
+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.
 
-Cpulimit is a program that attempts to limit the cpu usage of a process (expressed in percentage, not in cpu time). This is useful to control batch jobs, when you don't want they eat too much cpu. It does not act on the nice value or other scheduling priority stuff, but on the real cpu usage. Also, it is able to adapt itself to the overall system load, dynamically and quickly.
-The control of the used cpu amount is done sending continue and stop POSIX signals to processes.
-All the children processes and threads of the specified process will share the same percent of cpu.
+LimitCPU is the direct child of CPUlimit, a creation of Angelo Marletta,
+which can be found at http://cpulimit.sourceforge.net.
 
-Developed by Angelo Marletta.
-Please send your feedback, bug reports, feature requests to marlonx80 at hotmail dot com.
 
-===========
-Changelog:
-===========
 
-- reorganization of the code, splitted in more source files
-- control function process_monitor() optimized by eliminating an unnecessary loop
-- experimental support for multiple control of children processes and threads
-  children detection algorithm seems heavy because of the amount of code,
-  but it's designed to be scalable when there are a lot of children processes
-- cpu count detection, i.e. if you have 4 cpu, it is possible to limit up to 400%
-- in order to avoid deadlock, cpulimit prevents to limit itself
-- option --path eliminated, use --exe instead both for absolute path and file name
-- deleted every setpriority(), (todo: set it just once)
-- minor enhancements and bugfixes
 
+Copying, License and Distribution
+===================================
 
-============================
-Get the latest source code:
-============================
+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.
 
-You can checkout the latest code from sourceforge Subversion repository, running:
 
-svn checkout https://cpulimit.svn.sourceforge.net/svnroot/cpulimit cpulimit
 
-Of course this is the development version, so you may experience bugs (please signal them!)
 
 
-==============
-Installation:
-==============
+Where to get LimitCPU
+==========================
 
-Run 'make' and place the executable file 'cpulimit' wherever you want.
-Type 'cpulimit --help' to get documentation on available options.
+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.3.tar.gz
+
+Then we run the makefile.
+
+cd cpulimit-1.3
+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.
 
diff --git a/cpulimit.1.gz b/cpulimit.1.gz
new file mode 100644 (file)
index 0000000..43ad455
Binary files /dev/null and b/cpulimit.1.gz differ
index eb796cd..29aeca1 100644 (file)
@@ -1,50 +1,26 @@
 /**
- *
- * cpulimit - a cpu limiter for Linux
- *
- * Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
- *
- * 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.
+ * 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.
  *
  **********************************************************************
  *
- * This is a simple program to limit the cpu usage of a process
+ * Simple program to limit the cpu usage of a process
  * If you modify this code, send me a copy please
  *
- * Date:    15/2/2008
- * Version: 1.2 alpha
- * Get the latest version at: http://cpulimit.sourceforge.net
+ * Author:  Angelo Marletta
+ * Date:    26/06/2005
+ * Version: 1.1
  *
- * Changelog:
- * - reorganization of the code, splitted in more source files
- * - control function process_monitor() optimized by eliminating an unnecessary loop
- * - experimental support for multiple control of children processes and threads
- *   children detection algorithm seems heavy because of the amount of code,
- *   but it's designed to be scalable when there are a lot of children processes
- * - cpu count detection, i.e. if you have 4 cpu, it is possible to limit up to 400%
- * - in order to avoid deadlock, cpulimit prevents to limit itself
- * - option --path eliminated, use --exe instead both for absolute path and file name
- * - deleted almost every setpriority(), just set it once at startup
- * - minor enhancements and bugfixes
+ * Modifications and updates by: Jesse Smith
+ * Date: May 4, 2011
+ * Version 1.2
  *
  */
 
 
 #include <getopt.h>
 #include <stdio.h>
-#include <fcntl.h>
 #include <stdlib.h>
 #include <time.h>
 #include <sys/time.h>
 #include <dirent.h>
 #include <errno.h>
 #include <string.h>
+#include <limits.h>    // for compatibility
 
-#include "process.h"
-#include "procutils.h"
-#include "list.h"
+
+//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) (a<b?a:b)
-#define MAX(a,b) (a>b?a:b)
-#define print_caption()        printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n")
+#define min(a,b) (a<b?a:b)
+#define max(a,b) (a>b?a:b)
 
-//control time slot in microseconds
-//each slot is splitted in a working slice and a sleeping slice
-#define TIME_SLOT 100000
+//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;
+}
 
-#define MAX_PRIORITY -10
+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");
+       }
 
-/* GLOBAL VARIABLES */
+       int i=0;
 
-//the "family"
-struct process_family pf;
-//pid of cpulimit
-int cpulimit_pid;
-//name of this program (maybe cpulimit...)
-char *program_name;
+       while(1) {
 
-/* CONFIGURATION VARIABLES */
+               DIR *dip;
+               struct dirent *dit;
 
-//verbose mode
-int verbose = 0;
-//lazy mode (exits if there is no process)
-int lazy = 0;
-
-//how many cpu do we have?
-int get_cpu_count()
-{
-       FILE *fd;
-       int cpu_count = 0;
-       char line[100];
-       fd = fopen("/proc/stat", "r");
-       if (fd < 0)
-               return 0; //are we running Linux??
-       while (fgets(line,sizeof(line),fd)!=NULL) {
-               if (strncmp(line, "cpu", 3) != 0) break;
-               cpu_count++;
-       }
-       fclose(fd);
-       return cpu_count - 1;
-}
+               //open a directory stream to /proc directory
+               if ((dip = opendir("/proc")) == NULL) {
+                       perror("opendir");
+                       return -1;
+               }
 
-//return t1-t2 in microseconds (no overflow checks, so better watch out!)
-inline unsigned long timediff(const struct timespec *t1,const struct timespec *t2)
-{
-       return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_nsec/1000 - t2->tv_nsec/1000);
-}
+               //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);
+                               }
+                       }
+               }
 
-//returns t1-t2 in microseconds
-inline unsigned long long tv_diff(struct timeval *t1, struct timeval *t2)
-{
-       return ((unsigned long long)(t1->tv_sec - t2->tv_sec)) * 1000000ULL + t1->tv_usec - t2->tv_usec;
-}
+               //close the dir stream and check for errors
+               if (closedir(dip) == -1) {
+                       perror("closedir");
+                       return -1;
+               }
 
-//SIGINT and SIGTERM signal handler
-void quit(int sig)
-{
-       //let all the processes continue if stopped
-       struct list_node *node = NULL;
-       for (node=pf.members.first; node!= NULL; node=node->next) {
-               struct process *p = (struct process*)(node->data);
-               process_close(p->history);
-               kill(p->pid, SIGCONT);
+               //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);
        }
-       //free all the memory
-       cleanup_process_family(&pf);
-       exit(0);
-}
 
-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 (implies -z)\n");
-       fprintf(stream, "      -e, --exe=FILE     name of the executable program file or absolute path name\n");
-       fprintf(stream, "   OPTIONS\n");
-       fprintf(stream, "      -l, --limit=N      percentage of cpu allowed from 0 to 100 (required)\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);
+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;
+
 }
 
-void limit_process(int pid, double limit)
-{
-       //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
-       struct timespec tsleep;
-       //when the last twork has started
-       struct timespec startwork;
-       //when the last twork has finished
-       struct timespec endwork;
-       //initialization
-       memset(&twork, 0, sizeof(struct timespec));
-       memset(&tsleep, 0, sizeof(struct timespec));
-       memset(&startwork, 0, sizeof(struct timespec));
-       memset(&endwork, 0, sizeof(struct timespec));   
-       //last working time in microseconds
-       unsigned long workingtime = 0;
-       int i = 0;
-
-       //build the family
-       create_process_family(&pf, pid);
-       struct list_node *node;
-       
-       if (verbose) printf("Members in the family owned by %d: %d\n", pf.father, pf.members.count);
-
-       //rate at which we are keeping active the processes (range 0-1)
-       //1 means that the process are using all the twork slice
-       double workingrate = -1;
+//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) {
 
-               if (i%200==0 && verbose) print_caption();
-
-               if (i%10==0) {
-                       //update the process family (checks only for new members)
-                       int new_children = update_process_family(&pf);
-                       if (new_children) {
-                               printf("%d new children processes detected (", new_children);
-                               int j;
-                               node = pf.members.last;
-                               for (j=0; j<new_children; j++) {
-                                       printf("%d", ((struct process*)(node->data))->pid);
-                                       if (j<new_children-1) printf(" ");
-                                       node = node->previous;
+               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);
+                                               }
+                                       }
                                }
-                               printf(")\n");
                        }
                }
 
-               //total cpu actual usage (range 0-1)
-               //1 means that the processes are using 100% cpu
-               double pcpu = -1;
-               //number of processes in the family
-               int pcount = 0;
-               
-               //estimate how much the controlled processes are using the cpu in the working interval
-               for (node=pf.members.first; node!=NULL; node=node->next) {
-                       struct process *proc = (struct process*)(node->data);
-                       if (process_monitor(proc->history)==-1) {
-                               //process is dead, remove it from family
-                               fprintf(stderr,"Process %d dead!\n", proc->pid);
-                               remove_process_from_family(&pf, proc->pid);
-                               continue;
+               //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);
                        }
-//printf("pid %d limit %f pcpu %f wrate %f\n", proc->pid, limit, proc->history->usage.pcpu, proc->history->usage.workingrate);
-                       if (proc->history->cpu_usage<0) {
-                               continue;
+                       else {
+                               printf("Warning: no target process found. Waiting for it...\n");
                        }
-                       if (pcpu<0) pcpu = 0;
-                       pcpu += proc->history->cpu_usage;
-                       pcount++;
                }
 
-               //adjust work and sleep time slices
-               if (pcpu < 0) {
-                       //it's the 1st cycle, initialize workingrate
-                       pcpu = limit;
-                       workingrate = limit;
-                       twork.tv_nsec = TIME_SLOT*limit*1000;
-               }
-               else {
-                       //adjust workingrate
-                       workingrate = MIN(workingrate / pcpu * limit, 1);
-                       twork.tv_nsec = TIME_SLOT*1000*workingrate;
-               }
-               tsleep.tv_nsec = TIME_SLOT*1000-twork.tv_nsec;
+               //sleep for a while
+               sleep(2);
+       }
 
-//printf("%lf %lf\n", workingrate, pcpu);
+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;
 
-               if (verbose && i%10==0 && i>0) {
-                       printf("%0.2lf%%\t%6ld us\t%6ld us\t%0.2lf%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100);
-               }
+}
 
-               //resume processes
-               for (node=pf.members.first; node!=NULL; node=node->next) {
-                       struct process *proc = (struct process*)(node->data);
-                       if (kill(proc->pid,SIGCONT)!=0) {
-                               //process is dead, remove it from family
-                               fprintf(stderr,"Process %d dead!\n", proc->pid);
-                               remove_process_from_family(&pf, proc->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);
+}
 
-               //now processes are free to run (same working slice for all)
-               clock_gettime(CLOCK_REALTIME,&startwork);
-               nanosleep(&twork,NULL);
-               clock_gettime(CLOCK_REALTIME,&endwork);
-               workingtime = timediff(&endwork,&startwork);
-
-               if (tsleep.tv_nsec>0) {
-                       //stop only if tsleep>0, instead it's useless
-                       for (node=pf.members.first; node!=NULL; node=node->next) {
-                               struct process *proc = (struct process*)(node->data);
-                               if (kill(proc->pid,SIGSTOP)!=0) {
-                                       //process is dead, remove it from family
-                                       fprintf(stderr,"Process %d dead!\n", proc->pid);
-                                       remove_process_from_family(&pf, proc->pid);
-                               }
-                       }
-                       //now the processes are sleeping
-                       nanosleep(&tsleep,NULL);
-               }
-               i++;
+//get jiffies count from /proc filesystem
+int getjiffies(int pid) {
+       static char stat[20];
+       static char buffer[1024];
+       sprintf(stat,"/proc/%d/stat",pid);
+       FILE *f=fopen(stat,"r");
+       if (f==NULL) return -1;
+       fgets(buffer,sizeof(buffer),f);
+       fclose(f);
+       char *p=buffer;
+       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;
+}
+
+//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;
        }
-       cleanup_process_family(&pf);
+       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);
-       cpulimit_pid = getpid();
-
-       //argument variables
-       const char *exe = NULL;
-       int perclimit = 0;
-       int pid_ok = 0;
-       int process_ok = 0;
-       int limit_ok = 0;
-       int pid = 0;
-
+        int run_in_background = 0;
        //parse arguments
        int next_option;
-    int option_index = 0;
-       //A string listing valid short options letters
-       const char* short_options = "p:e:l:vzh";
-       //An array describing valid long options
+       /* 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' },
-               { "limit",      required_argument, NULL,     'l' },
-               { "verbose",    no_argument,       &verbose, 'v' },
-               { "lazy",       no_argument,       &lazy,    'z' },
-               { "help",       no_argument,       NULL,     'h' },
-               { 0,            0,                 0,         0  }
+               { "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, &option_index);
+               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);
-                               //todo: verify pid is valid
-                               pid_ok = 1;
-                               process_ok = 1;
+                               pid=atoi(optarg);
+                               pid_ok=1;
+                                lazy = 1;
                                break;
                        case 'e':
-                               exe = optarg;
-                               process_ok = 1;
+                               exe=optarg;
+                               process_ok=1;
+                               break;
+                       case 'P':
+                               path=optarg;
+                               process_ok=1;
                                break;
                        case 'l':
-                               perclimit = atoi(optarg);
-                               limit_ok = 1;
+                               perclimit=atoi(optarg);
+                               limit_ok=1;
                                break;
                        case 'v':
-                               verbose = 1;
+                               verbose=1;
                                break;
                        case 'z':
-                               lazy = 1;
+                               lazy=1;
                                break;
                        case 'h':
-                               print_usage(stdout, 1);
+                               print_usage (stdout, 1);
                                break;
                        case '?':
-                               print_usage(stderr, 1);
+                               print_usage (stderr, 1);
                                break;
                        case -1:
                                break;
@@ -339,98 +430,183 @@ int main(int argc, char **argv) {
                }
        } while(next_option != -1);
 
-       if (pid!=0) {
-               lazy = 1;
-       }
-       
-       if (pid_ok && (pid<=1 || pid>=65536)) {
-               fprintf(stderr,"Error: Invalid value for argument PID\n");
-               print_usage(stderr, 1);
+       if (!process_ok && !pid_ok) {
+               fprintf(stderr,"Error: You must specify a target process\n");
+               print_usage (stderr, 1);
                exit(1);
        }
-       
-       if (!process_ok) {
-               fprintf(stderr,"Error: You must specify a target process, either by name or by PID\n");
-               print_usage(stderr, 1);
-               exit(1);
-       }
-       if (pid_ok && exe!=NULL) {
-               fprintf(stderr, "Error: You must specify exactly one process, either by name or by PID\n");
-               print_usage(stderr, 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 percentage\n");
-               print_usage(stderr, 1);
+               fprintf(stderr,"Error: You must specify a cpu limit\n");
+               print_usage (stderr, 1);
                exit(1);
        }
-       double limit = perclimit/100.0;
-       int cpu_count = get_cpu_count();
-       printf("%d cpu detected\n", cpu_count);
-       if (limit<0 || limit >cpu_count) {
-               fprintf(stderr,"Error: limit must be in the range 0-%d00\n", cpu_count);
-               print_usage(stderr, 1);
+       float limit=perclimit/100.0;
+       if (limit<0 || limit >1) {
+               fprintf(stderr,"Error: limit must be in the range 0-100\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);
-
-       //try to renice with the best value
-       int old_priority = getpriority(PRIO_PROCESS, 0);
-       int priority = old_priority;
-       while (setpriority(PRIO_PROCESS, 0, priority-1) == 0 && priority>MAX_PRIORITY) {
-               priority--;     
-       }
-       if (priority != old_priority) {
-               printf("Priority changed to %d\n", priority);
+       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 {
-               printf("Warning: Cannot change priority. Run as root for best results.\n");
+               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) {
-               //look for the target process..or wait for it
-               int ret = 0;
-               if (pid_ok) {
-                       //search by pid
-                       ret = look_for_process_by_pid(pid);
-                       if (ret == 0) {
-                               printf("No process found\n");
-                       }
-                       else if (ret < 0) {
-                               printf("Process found but you aren't allowed to control it\n");
-                       }
+
+               //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;          
                }
-               else {
-                       //search by file or path name
-                       ret = look_for_process_by_name(exe);
-                       if (ret == 0) {
-                               printf("No process found\n");
-                       }
-                       else if (ret < 0) {
-                               printf("Process found but you aren't allowed to control it\n");
-                       }
-                       else {
-                               pid = ret;
-                       }
+
+               //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);
                }
-               if (ret > 0) {
-                       if (ret == cpulimit_pid) {
-                               printf("Process %d is cpulimit itself! Aborting to avoid deadlock\n", ret);
-                               exit(1);
+               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;
                        }
-                       printf("Process %d found\n", pid);
-                       //control
-                       limit_process(pid, limit);
                }
-               if (lazy) {
-                       printf("Giving up...\n");
-                       break;
+
+               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
                }
-               sleep(2);
+               i++;
        }
-       
-       return 0;
-}
 
+}
diff --git a/list.c b/list.c
deleted file mode 100644 (file)
index 193eac9..0000000
--- a/list.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
-*
-* cpulimit - a cpu limiter for Linux
-*
-* Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
-*
-* 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.
-*
-**********************************************************************
-*
-* Dynamic list implementation
-*
-*/
-
-#include "list.h"
-
-#define EMPTYLIST NULL
-
-void init_list(struct list *l,int keysize) {
-       l->first=l->last=NULL;
-       l->keysize=keysize;
-       l->count=0;
-}
-
-struct list_node *add_elem(struct list *l,void *elem) {
-       struct list_node *newnode=malloc(sizeof(struct list_node));
-       newnode->data=elem;
-       newnode->previous=l->last;
-       newnode->next=NULL;
-       if (l->count==0) {
-               l->first=l->last=newnode;
-       }
-       else {
-               l->last->next=newnode;
-               l->last=newnode;
-       }
-       l->count++;
-       return newnode;
-}
-
-void delete_node(struct list *l,struct list_node *node) {
-       if (l->count==1) {
-               l->first=l->last=NULL;
-       }
-       else if (node==l->first) {
-               node->next->previous=NULL;
-               l->first=node->next;
-       }
-       else if (node==l->last) {
-               node->previous->next=NULL;
-               l->last=node->previous;
-       }
-       else {
-               node->previous->next=node->next;
-               node->next->previous=node->previous;
-       }
-       l->count--;
-       free(node);
-       node = NULL;
-}
-
-void destroy_node(struct list *l,struct list_node *node) {
-       free(node->data);
-       node->data=NULL;
-       delete_node(l,node);
-}
-
-int is_EMPTYLIST_list(struct list *l) {
-       return (l->count==0?TRUE:FALSE);
-}
-
-int get_list_count(struct list *l) {
-       return l->count;
-}
-
-void *first_elem(struct list *l) {
-       return l->first->data;
-}
-
-struct list_node *first_node(struct list *l) {
-       return l->first;
-}
-
-void *last_elem(struct list *l) {
-       return l->last->data;
-}
-
-struct list_node *last_node(struct list *l) {
-       return l->last;
-}
-
-struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length) {
-       struct list_node *tmp;
-       tmp=l->first;
-       while(tmp!=NULL) {
-               if(!memcmp(tmp->data+offset,elem,length==0?l->keysize:length)) return (tmp);
-               tmp=tmp->next;
-       }
-       return EMPTYLIST;
-}
-
-struct list_node *locate_node(struct list *l,void *elem) {
-       return(xlocate_node(l,elem,0,0));
-}
-
-void *xlocate_elem(struct list *l,void *elem,int offset,int length) {
-       struct list_node *node=xlocate_node(l,elem,offset,length);
-       return(node==NULL?NULL:node->data);
-}
-
-void *locate_elem(struct list *l,void *elem) {
-       return(xlocate_elem(l,elem,0,0));
-}
-
-void flush_list(struct list *l) {
-       struct list_node *tmp;
-       while(l->first!=EMPTYLIST) {
-               tmp=l->first;
-               l->first=l->first->next;
-               free(tmp);
-               tmp=NULL;
-       }
-       l->last=EMPTYLIST;
-       l->count=0;
-}
-
-void destroy_list(struct list *l) {
-       struct list_node *tmp;
-       while(l->first!=EMPTYLIST) {
-               tmp=l->first;
-               l->first=l->first->next;
-               free(tmp->data);
-               tmp->data=NULL;
-               free(tmp);
-               tmp=NULL;
-       }
-       l->last=EMPTYLIST;
-       l->count=0;
-}
diff --git a/list.h b/list.h
deleted file mode 100644 (file)
index d505665..0000000
--- a/list.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- *
- * cpulimit - a cpu limiter for Linux
- *
- * Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
- *
- * 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.
- *
- **********************************************************************
- *
- * Dynamic list interface
- *
- */
-
-#ifndef __LIST__
-
-#define __LIST__
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifndef  TRUE
-    #define TRUE 1
-    #define FALSE 0
-#endif
-
-struct list_node {
-    //pointer to the content of the node
-    void *data;
-    //pointer to previous node
-    struct list_node *previous;
-    //pointer to next node
-    struct list_node *next;
-};
-
-struct list {
-    //first node
-    struct list_node *first;
-    //last node
-    struct list_node *last;
-    //size of the search key in bytes
-    int keysize;
-    //element count
-    int count;
-};
-
-/*
- * Initialize a list, with a specified key size
- */
-void init_list(struct list *l,int keysize);
-
-/*
- * Add a new element at the end of the list
- * return the pointer to the new node
- */
-struct list_node *add_elem(struct list *l,void *elem);
-
-/*
- * Delete a node
- */
-void delete_node(struct list *l,struct list_node *node);
-
-/*
- * Delete a node from the list, even the content pointed by it
- * Use only when the content is a dynamically allocated pointer
- */
-void destroy_node(struct list *l,struct list_node *node);
-
-/*
- * Check whether a list is empty or not
- */
-int is_empty_list(struct list *l);
-
-/*
- * Return the element count of the list
- */
-int get_list_count(struct list *l);
-
-/*
- * Return the first element (content of the node) from the list
- */
-void *first_elem(struct list *l);
-
-/*
- * Return the first node from the list
- */
-struct list_node *first_node(struct list *l);
-
-/*
- * Return the last element (content of the node) from the list
- */
-void *last_elem(struct list *l);
-
-/*
- * Return the last node from the list
- */
-struct list_node *last_node(struct list *l);
-
-/*
- * Search an element of the list by content
- * the comparison is done from the specified offset and for a specified length
- * if offset=0, the comparison starts from the address pointed by data
- * if length=0, default keysize is used for length
- * if the element is found, return the node address
- * else return NULL
- */
-struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length);
-
-/*
- * The same of xlocate_node(), but return the content of the node
- */
-void *xlocate_elem(struct list *l,void *elem,int offset,int length);
-
-/*
- * The same of calling xlocate_node() with offset=0 and length=0
- */
-struct list_node *locate_node(struct list *l,void *elem);
-
-/*
- * The same of locate_node, but return the content of the node
- */
-void *locate_elem(struct list *l,void *elem);
-
-/*
- * Delete all the elements in the list
- */
-void flush_list(struct list *l);
-
-/*
- * Delete every element in the list, and free the memory pointed by all the node data
- */
-void destroy_list(struct list *l);
-
-#endif
diff --git a/process.c b/process.c
deleted file mode 100644 (file)
index 4131ce4..0000000
--- a/process.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/**
- *
- * cpulimit - a cpu limiter for Linux
- *
- * Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
- *
- * 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.
- */
-
-//TODO: add documentation to public functions
-
-#include "process.h"
-
-int process_init(struct process_history *proc, int pid)
-{
-       //test /proc file descriptor for reading
-       sprintf(proc->stat_file, "/proc/%d/stat", pid);
-       FILE *fd = fopen(proc->stat_file, "r");
-       if (fd == NULL) return 1;
-       fclose(fd);
-       //init properties
-       proc->pid = pid;
-       proc->cpu_usage = 0;
-       memset(&(proc->last_sample), 0, sizeof(struct timespec));
-       proc->last_jiffies = -1;
-       return 0;
-}
-
-//return t1-t2 in microseconds (no overflow checks, so better watch out!)
-static inline unsigned long timediff(const struct timespec *t1,const struct timespec *t2)
-{
-       return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_nsec/1000 - t2->tv_nsec/1000);
-}
-
-static int get_jiffies(struct process_history *proc) {
-       FILE *f = fopen(proc->stat_file, "r");
-       if (f==NULL) return -1;
-       fgets(proc->buffer, sizeof(proc->buffer),f);
-       fclose(f);
-       char *p = proc->buffer;
-       p = memchr(p+1,')', sizeof(proc->buffer) - (p-proc->buffer));
-       int sp = 12;
-       while (sp--)
-               p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer));
-       //user mode jiffies
-       int utime = atoi(p+1);
-       p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer));
-       //kernel mode jiffies
-       int ktime = atoi(p+1);
-       return utime+ktime;
-}
-
-#define ALFA 0.08
-
-int process_monitor(struct process_history *proc)
-{
-       int j = get_jiffies(proc);
-       if (j<0) return -1; //error retrieving jiffies count (maybe the process is dead)
-       struct timespec now;
-       clock_gettime(CLOCK_REALTIME, &now);
-       if (proc->last_jiffies==-1) {
-               //store current time
-               proc->last_sample = now;
-               //store current jiffies
-               proc->last_jiffies = j;
-               //it's the first sample, cannot figure out the cpu usage
-               proc->cpu_usage = -1;
-               return 0;
-       }
-       //time from previous sample (in ns)
-       long dt = timediff(&now, &(proc->last_sample));
-       //how many jiffies in dt?
-       double max_jiffies = dt * HZ / 1000000.0;
-       double sample = (j - proc->last_jiffies) / max_jiffies;
-       if (proc->cpu_usage == -1) {
-               //initialization
-               proc->cpu_usage = sample;
-       }
-       else {
-               //adaptative adjustment
-               proc->cpu_usage = (1-ALFA) * proc->cpu_usage + ALFA * sample;
-       }
-
-       //store current time
-       proc->last_sample = now;
-       //store current jiffies
-       proc->last_jiffies = j;
-       
-       return 0;
-}
-
-int process_close(struct process_history *proc)
-{
-       if (kill(proc->pid,SIGCONT)!=0) {
-               fprintf(stderr,"Process %d is already dead!\n", proc->pid);
-       }
-       proc->pid = 0;
-       return 0;
-}
diff --git a/process.h b/process.h
deleted file mode 100644 (file)
index 5a73cc2..0000000
--- a/process.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- *
- * cpulimit - a cpu limiter for Linux
- *
- * Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
- *
- * 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.
- */
-
-#ifndef __PROCESS_H
-
-#define __PROCESS_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-//kernel time resolution timer interrupt frequency in Hertz
-#define HZ sysconf(_SC_CLK_TCK)
-
-//process descriptor
-struct process_history {
-       //the PID of the process
-       int pid;
-       //name of /proc/PID/stat file
-       char stat_file[20];
-       //read buffer for /proc filesystem
-       char buffer[1024];
-       //timestamp when last_j and cpu_usage was calculated
-       struct timespec last_sample;
-       //total number of jiffies used by the process at time last_sample
-       int last_jiffies;
-       //cpu usage estimation (value in range 0-1)
-       double cpu_usage;
-};
-
-int process_init(struct process_history *proc, int pid);
-
-int process_monitor(struct process_history *proc);
-
-int process_close(struct process_history *proc);
-
-#endif
diff --git a/procutils.c b/procutils.c
deleted file mode 100644 (file)
index 989c555..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-/**
- *
- * cpulimit - a cpu limiter for Linux
- *
- * Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
- *
- * 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.
- */
-
-#include <sys/utsname.h>
-#include "procutils.h"
-
-/* PROCESS STATISTICS FUNCTIONS */
-
-// returns pid of the parent process
-static int getppid_of(int pid)
-{
-       char file[20];
-       char buffer[1024];
-       sprintf(file, "/proc/%d/stat", pid);
-       FILE *fd = fopen(file, "r");
-               if (fd==NULL) return -1;
-       fgets(buffer, sizeof(buffer), fd);
-       fclose(fd);
-       char *p = buffer;
-       p = memchr(p+1,')', sizeof(buffer) - (p-buffer));
-       int sp = 2;
-       while (sp--)
-               p = memchr(p+1,' ',sizeof(buffer) - (p-buffer));
-       //pid of the parent process
-       int ppid = atoi(p+1);
-       return ppid;
-}
-
-// returns the start time of a process (used with pid to identify a process)
-static int get_starttime(int pid)
-{
-       char file[20];
-       char buffer[1024];
-       sprintf(file, "/proc/%d/stat", pid);
-       FILE *fd = fopen(file, "r");
-               if (fd==NULL) return -1;
-       fgets(buffer, sizeof(buffer), fd);
-       fclose(fd);
-       char *p = buffer;
-       p = memchr(p+1,')', sizeof(buffer) - (p-buffer));
-       int sp = 20;
-       while (sp--)
-               p = memchr(p+1,' ',sizeof(buffer) - (p-buffer));
-       //start time of the process
-       int time = atoi(p+1);
-       return time;
-}
-
-// detects whether a process is a kernel thread or not
-static int is_kernel_thread(int pid)
-{
-       static char statfile[20];
-       static char buffer[64];
-       int ret;
-       sprintf(statfile, "/proc/%d/statm", pid);
-       FILE *fd = fopen(statfile, "r");
-       if (fd==NULL) return -1;
-       fgets(buffer, sizeof(buffer), fd);
-       ret = strncmp(buffer,"0 0 0",3)==0;
-       fclose(fd);
-       return ret;
-}
-
-// returns 1 if pid is a user process, 0 otherwise
-static int process_exists(int pid) {
-       static char statfile[20];
-       static char buffer[64];
-       int ret;
-       sprintf(statfile, "/proc/%d/statm", pid);
-       FILE *fd = fopen(statfile, "r");
-       if (fd==NULL) return 0;
-       fgets(buffer, sizeof(buffer), fd);
-       ret = strncmp(buffer,"0 0 0",3)!=0;
-       fclose(fd);
-       return ret;
-}
-
-/* PID HASH FUNCTIONS */
-
-static int hash_process(struct process_family *f, struct process *p)
-{
-       int ret;
-       struct list **l = &(f->hashtable[pid_hashfn(p->pid)]);
-       if (*l == NULL) {
-               //there is no process in this hashtable item
-               //allocate the list
-               *l = malloc(sizeof(struct list));
-               init_list(*l, 4);
-               add_elem(*l, p);
-               ret = 0;
-               f->count++;
-       }
-       else {
-               //list already exists
-               struct process *tmp = (struct process*)locate_elem(*l, p);
-               if (tmp != NULL) {
-                       //TODO: should free() something? tmp? p?
-                       //update process info
-                       memcpy(tmp, p, sizeof(struct process));
-                       free(p);
-                       p = NULL;
-                       ret = 1;
-               }
-               else {
-                       //add new process
-                       add_elem(*l, p);
-                       ret = 0;
-                       f->count++;
-               }
-       }
-       return ret;
-}
-
-static void unhash_process(struct process_family *f, int pid) {
-       //remove process from hashtable
-       struct list **l = &(f->hashtable[pid_hashfn(pid)]);
-       if (*l == NULL)
-               return; //nothing done
-       struct list_node *node = locate_node(*l, &pid);
-       if (node != NULL)
-               destroy_node(*l, node);
-       f->count--;
-}
-
-static struct process *seek_process(struct process_family *f, int pid)
-{
-       struct list **l = &(f->hashtable[pid_hashfn(pid)]);
-       return (*l != NULL) ? (struct process*)locate_elem(*l, &pid) : NULL;
-}
-
-/*
-static int is_member(struct process_family *f, int pid) {
-       struct process *p = seek_process(f, pid);
-       return (p!=NULL && p->member);
-}
-
-static int exists(struct process_family *f, int pid) {
-       struct process *p = seek_process(f, pid);
-       return p!=NULL;
-}
-*/
-
-/* PROCESS ITERATOR STUFF */
-
-// creates an object that browse all running processes
-static int init_process_iterator(struct process_iterator *i) {
-       //open a directory stream to /proc directory
-       if ((i->dip = opendir("/proc")) == NULL) {
-               perror("opendir");
-               return -1;
-       }
-       return 0;
-}
-
-// reads the next user process from /process
-// automatic closing if the end of the list is reached
-static int read_next_process(struct process_iterator *i) {
-       //read in from /proc and seek for process dirs
-       int pid = 0;
-       while ((i->dit = readdir(i->dip)) != NULL) {
-               pid = atoi(i->dit->d_name);
-               if (pid<=0 || is_kernel_thread(pid))
-                       continue;
-               //return the first found process
-               break;
-       }
-       if (pid == 0) {
-               //no more processes, release resources
-               closedir(i->dip);
-       }
-       return pid;
-}
-
-/* PUBLIC FUNCTIONS */
-
-// searches for all the processes derived from father and stores them
-// in the process family struct
-int create_process_family(struct process_family *f, int father)
-{
-       //process list initialization (4 bytes key)
-       init_list(&(f->members), 4);
-       //hashtable initialization
-       memset(&(f->hashtable), 0, sizeof(f->hashtable));
-       f->count = 0;
-       f->father = father;
-       //process iterator
-       struct process_iterator iter;
-       init_process_iterator(&iter);
-       int pid = 0;
-       while ((pid = read_next_process(&iter))) {
-               //check if process belongs to the family
-               int ppid = pid;
-               //TODO: optimize adding also these parents, and continue if process is already present
-               while(ppid!=1 && ppid!=father) {
-                       ppid = getppid_of(ppid);
-               }
-               //allocate and insert the process
-               struct process *p = malloc(sizeof(struct process));
-               p->pid = pid;
-               p->starttime = get_starttime(pid);
-               if (ppid==1) {
-                       //the init process
-                       p->member = 0;
-               }
-               else if (pid != getpid()) {
-                       //add to members (but exclude the current cpulimit process!)
-                       p->member = 1;
-                       add_elem(&(f->members), p);
-               }
-               //init history
-               p->history = malloc(sizeof(struct process_history));
-               process_init(p->history, pid);
-               //add to hashtable
-               hash_process(f, p);
-       }
-       return 0;
-}
-
-// checks if there are new processes born in the specified family
-// if any they are added to the members list
-// the number of new born processes is returned
-int update_process_family(struct process_family *f)
-{
-       int ret = 0;
-       //process iterator
-       struct process_iterator iter;
-       init_process_iterator(&iter);
-       int pid = 0;
-       while ((pid = read_next_process(&iter))) {
-               struct process *newp = seek_process(f, pid);
-               if (newp != NULL) continue; //already known //TODO: what if newp is a new process with the same PID??
-               //the process is new, check if it belongs to the family
-               int ppid = getppid_of(pid);
-               //search the youngest known ancestor of the process
-               struct process *ancestor = NULL;
-               while((ancestor=seek_process(f, ppid))==NULL) {
-                       ppid = getppid_of(ppid);
-               }
-               if (ancestor == NULL) {
-                       //this should never happen! if does, find and correct the bug
-                       fprintf(stderr, "Fatal bug! Process %d is without parent\n", pid);
-                       exit(1);
-               }
-               //allocate and insert the process
-               struct process *p = malloc(sizeof(struct process));
-               p->pid = pid;
-               p->starttime = get_starttime(pid);
-               if (ancestor->member) {
-                       //add to members
-                       p->member = 1;
-                       add_elem(&(f->members), p);
-                       ret++;
-               }
-               else {
-                       //not a member
-                       p->member = 0;
-               }
-               //init history
-               p->history = malloc(sizeof(struct process_history));
-               process_init(p->history, pid);
-               //add to hashtable
-               hash_process(f, p);
-       }
-       return ret;
-}
-
-// removes a process from the family by its pid
-void remove_process_from_family(struct process_family *f, int pid)
-{
-       struct list_node *node = locate_node(&(f->members), &pid);
-       if (node != NULL) {
-               struct process *p = (struct process*)(node->data);
-               free(p->history);
-               p->history = NULL;
-               delete_node(&(f->members), node);
-       }
-       unhash_process(f, pid);
-}
-
-// free the heap memory used by a process family
-void cleanup_process_family(struct process_family *f)
-{
-       int i;
-       int size = sizeof(f->hashtable) / sizeof(struct process*);
-       for (i=0; i<size; i++) {
-               if (f->hashtable[i] != NULL) {
-                       //free() history for each process
-                       struct list_node *node = NULL;
-                       for (node=f->hashtable[i]->first; node!=NULL; node=node->next) {
-                               struct process *p = (struct process*)(node->data);
-                               free(p->history);
-                               p->history = NULL;
-                       }
-                       destroy_list(f->hashtable[i]);
-                       free(f->hashtable[i]);
-                       f->hashtable[i] = NULL;
-               }
-       }
-       flush_list(&(f->members));
-       f->count = 0;
-       f->father = 0;
-}
-
-// look for a process by pid
-// search_pid   : pid of the wanted process
-// return:  pid of the found process, if it is found
-//          0, if it's not found
-//          negative pid, if it is found but it's not possible to control it
-int look_for_process_by_pid(int pid)
-{
-       if (process_exists(pid))
-               return (kill(pid,SIGCONT)==0) ? pid : -pid;
-       return 0;
-}
-
-// look for a process with a given name
-// process: the name of the wanted process. it can be an absolute path name to the executable file
-//         or just the file name
-// return:  pid of the found process, if it is found
-//         0, if it's not found
-//         negative pid, if it is found but it's not possible to control it
-int look_for_process_by_name(const char *process)
-{
-       //the name of /proc/pid/exe symbolic link pointing to the executable file
-       char exelink[20];
-       //the name of the executable file
-       char exepath[PATH_MAX+1];
-       //whether the variable process is the absolute path or not
-       int is_absolute_path = process[0] == '/';
-       //flag indicating if the a process with given name was found
-       int found = 0;
-
-       //process iterator
-       struct process_iterator iter;
-       init_process_iterator(&iter);
-       int pid = 0;
-       while ((pid = read_next_process(&iter))) {
-               //read the executable link
-               sprintf(exelink,"/proc/%d/exe",pid);
-               int size = readlink(exelink, exepath, sizeof(exepath));
-               if (size>0) {
-                       found = 0;
-                       if (is_absolute_path && strncmp(exepath, process, size)==0 && size==strlen(process)) {
-                               //process found
-                               found = 1;
-                       }
-                       else {
-                               //process found
-                               if (strncmp(exepath + size - strlen(process), process, strlen(process))==0) {
-                                       found = 1;
-                               }
-                       }
-                       if (found==1) {
-                               if (kill(pid,SIGCONT)==0) {
-                                       //process is ok!
-                                       break;
-                               }
-                               else {
-                                       //we don't have permission to send signal to that process
-                                       //so, don't exit from the loop and look for another one with the same name
-                                       found = -1;
-                               }
-                       }
-               }
-       }
-       if (found == 1) {
-               //ok, the process was found
-               return pid;
-       }
-       else if (found == 0) {
-               //no process found
-               return 0;
-       }
-       else if (found == -1) {
-               //the process was found, but we haven't permission to control it
-               return -pid;
-       }
-       //this MUST NOT happen
-       return 0;
-}
diff --git a/procutils.h b/procutils.h
deleted file mode 100644 (file)
index 8a1deb2..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- *
- * cpulimit - a cpu limiter for Linux
- *
- * Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
- *
- * 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.
- */
-
-#ifndef __PROCUTILS_H
-
-#define __PROCUTILS_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <string.h>
-#include "list.h"
-#include "process.h"
-
-#define PIDHASH_SZ 1024
-#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
-
-// a hierarchy of processes
-struct process_family {
-       //the (god)father of the process family
-       int father;
-       //process list (father process is the first element)
-       //elements are struct process
-       struct list members;
-       //non-members list
-       //hashtable with all the processes (array of struct list of struct process)
-       struct list *hashtable[PIDHASH_SZ];
-       //total process count
-       int count;
-};
-
-// process descriptor
-struct process {
-       //pid of the process
-       int pid;
-       //start time
-       int starttime;
-       //is member of the family?
-       int member;
-       struct process_history *history;
-};
-
-// object to enumerate running processes
-struct process_iterator {
-       DIR *dip;
-       struct dirent *dit;     
-};
-
-// searches for all the processes derived from father and stores them
-// in the process family struct
-int create_process_family(struct process_family *f, int father);
-
-// checks if there are new processes born in the specified family
-// if any they are added to the members list
-// the number of new born processes is returned
-int update_process_family(struct process_family *f);
-
-// removes a process from the family by its pid
-void remove_process_from_family(struct process_family *f, int pid);
-
-// free the heap memory used by a process family
-void cleanup_process_family(struct process_family *f);
-
-// searches a process given the name of the executable file, or its absolute path
-// returns the pid, or 0 if it's not found
-int look_for_process_by_name(const char *process);
-
-// searches a process given its pid
-// returns the pid, or 0 if it's not found
-int look_for_process_by_pid(int pid);
-
-#endif
diff --git a/ptest.c b/ptest.c
deleted file mode 100644 (file)
index 64b124d..0000000
--- a/ptest.c
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#define N 8
-
-//simple program to test cpulimit
-int main()
-{
-       printf("Parent: PID %d\n", getpid());
-       getchar();
-       sleep(1);
-       int i;
-       int children[N];
-       for (i=0;i<N;i++) {
-               int pid = fork();
-               if (pid>0) {
-                       //parent code
-                       children[i] = pid;
-                       printf("Child %d created\n", pid);
-               }
-               else if (pid==0) {
-                       while(1);
-                       //child code
-//                     while(1) {
-                               //random generator initialization
-                               struct timeval t;
-                               gettimeofday(&t, NULL);
-                               srandom(t.tv_sec + t.tv_usec + getpid());
-                               int loop = random() % 1000000;
-                               printf("start\n");
-                               int i,j;
-                               for (i=0;i<1000;i++)
-                                       for (j=0;j<loop;j++);
-                               printf("stop\n");
-                               int time = random() % 10000000;
-       //                      printf("Child %d wait %d\n", getpid(), time);
-                               usleep(time);
-                               printf("Child %d terminated\n", getpid());
-//                     }
-                       exit(0);
-               }
-               else {
-                       fprintf(stderr, "fork() failed!\n");
-               }
-       }
-       for (i=0;i<N;i++) {
-               int status;
-               waitpid(children[i], &status, 0);
-       }
-       sleep(1);
-       return 0;
-}