source: ocsinventory-agent/trunk/fuentes/tools/macosx/ocsng_app-xcode/GetPID.c @ 468

Last change on this file since 468 was 468, checked in by mabarracus, 4 years ago

Copyt trusty code

  • Property svn:executable set to *
File size: 20.7 KB
Line 
1/*
2        File:           GetPID.c
3       
4        Description:    This file provides a simple API to do process PID lookup based on process name.
5       
6        Author:         Chad Jones
7
8        Copyright:      © Copyright 2003 Apple Computer, Inc. All rights reserved.
9       
10        Disclaimer:     IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
11                                ("Apple") in consideration of your agreement to the following terms, and your
12                                use, installation, modification or redistribution of this Apple software
13                                constitutes acceptance of these terms.  If you do not agree with these terms,
14                                please do not use, install, modify or redistribute this Apple software.
15
16                                In consideration of your agreement to abide by the following terms, and subject
17                                to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
18                                copyrights in this original Apple software (the "Apple Software"), to use,
19                                reproduce, modify and redistribute the Apple Software, with or without
20                                modifications, in source and/or binary forms; provided that if you redistribute
21                                the Apple Software in its entirety and without modifications, you must retain
22                                this notice and the following text and disclaimers in all such redistributions of
23                                the Apple Software.  Neither the name, trademarks, service marks or logos of
24                                Apple Computer, Inc. may be used to endorse or promote products derived from the
25                                Apple Software without specific prior written permission from Apple.  Except as
26                                expressly stated in this notice, no other rights or licenses, express or implied,
27                                are granted by Apple herein, including but not limited to any patent rights that
28                                may be infringed by your derivative works or by other works in which the Apple
29                                Software may be incorporated.
30
31                                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
32                                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
33                                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34                                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
35                                COMBINATION WITH YOUR PRODUCTS.
36
37                                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
38                                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
39                                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40                                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
41                                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
42                                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
43                                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44                               
45        Change History (most recent first):
46*/
47
48#include "GetPID.h"
49
50#include <errno.h>
51#include <string.h>
52#include <sys/sysctl.h>
53
54/*****************************************************
55 * GetAllPIDsForProcessName
56 *****************************************************
57 * Purpose:  This functions purpose is to lookup a BSD
58 * process PID given the BSD process name.  This function may
59 * potentially return multiple PIDs for a given BSD process name
60 * since several processes can have the same BSD process name.
61 *
62 * Parameters:
63 *      ProcessName             A constant C-string.  On calling
64 * GetAllPIDsForProcessName this variable holds the BSD process name
65 * used to do the process lookup.  Note that the process name you need
66 * to pass is the name of the BSD executable process.  If trying
67 * to find the PID of an regular OSX application you will need to pass the
68 * name of the actual BSD executable inside an application bundle (rather
69 * than the bundle name itself).  In any case as a user you can find the
70 * BSD process name of any process (including OSX applications) by
71 * typing the command "ps -axcocommand,pid" in terminal.
72 *
73 *      ArrayOfReturnedPIDs     A pointer to a pre-allocated array of pid_t.
74 * On calling GetAllPIDsForProcessName this variable must be a pointer to a
75 * pre-allocated array of pid_t whos length (in number of pid_t entries) is defined
76 * in ArrayOfPIDsLength.  On successful return from GetAllPIDsForProcessName
77 * this array will hold the PIDs of all processes which have a matching process
78 * name to that specified in the ProcessName input variable.  The number of actual
79 * PIDs entered in the array starting at index zero will be the value returned
80 * in NumberOfMatchesFound.  On failed return if the error is a buffer overflow
81 * error then the buffer will be filled to the  max with PIDs which matched.
82 * Otherwise on failed return the state of the array will be undefined.  Note
83 * the returned PID array is not sorted and is listed in order of process encountered. 
84 *
85 *      NumberOfPossiblePIDsInArray     A unsigned integer.  On calling
86 * GetAllPIDsForProcessName this variable will hold the number of
87 * pre-allocated PID entries which are in the ArrayOfReturnedPIDs for this functions
88 * use.  Note this value must have a value greater than zero.
89 *
90 *      NumberOfMatchesFound    An unsigned integer.  On calling GetAllPIDsForProcessName
91 * this variable will point to a pre-allocated unsigned integer.  On return from
92 * GetAllPIDsForProcessName this variable will contain the number of PIDs contained in the
93 * ArrayOfReturnedPIDs.  On failed return the value of the variable will be undefined.
94 *
95 *      SysctlError     A pointer to a pre-allocated integer.  On failed return, this
96 * variable represents the error returned from the sysctl command.  On function
97 * success this variable will have a value specified by the sysctl based on the
98 * error that occurred.  On success the variable will have the value zero.
99 * Note this variable can also be NULL in which case the variable is ignored.
100 *
101 *      *Function Result*       A integer return value.
102 *                              See result codes listed below.
103 *              Result Codes:
104 *                0             Success.  A set of process PIDs were found and are located in
105 *                              ArrayOfReturnedPIDs array.
106 *               -1             Could not find a process with a matching process name
107 *                              (i.e. process not found).
108 *               -2             Invalid arguments passed.
109 *               -3             Unable to get the size of sysctl buffer required
110 *                              (consult SysctlError return value for more information)
111 *               -4             Unable to allocate memory to store BSD process information
112 *                              (consult SysctlError return value for more information)
113 *               -5             The array passed to hold the returned PIDs is not large enough
114 *                              to hold all PIDs of process with matching names.
115 *
116 *****************************************************/
117int GetAllPIDsForProcessName(const char* ProcessName, 
118                             pid_t ArrayOfReturnedPIDs[], 
119                             const unsigned int NumberOfPossiblePIDsInArray, 
120                             unsigned int* NumberOfMatchesFound,
121                             int* SysctlError)
122{
123    // --- Defining local variables for this function and initializing all to zero --- //
124    int mib[6] = {0,0,0,0,0,0}; //used for sysctl call.
125    int SuccessfullyGotProcessInformation;
126    size_t sizeOfBufferRequired = 0; //set to zero to start with.
127    int error = 0;
128    long NumberOfRunningProcesses = 0;
129    unsigned int Counter = 0;
130    struct kinfo_proc* BSDProcessInformationStructure = NULL;
131    pid_t CurrentExaminedProcessPID = 0;
132    char* CurrentExaminedProcessName = NULL;
133
134    // --- Checking input arguments for validity --- //
135    if (ProcessName == NULL) //need valid process name
136    {
137        return(kInvalidArgumentsError);
138    }
139
140    if (ArrayOfReturnedPIDs == NULL) //need an actual array
141    {
142        return(kInvalidArgumentsError);
143    }
144
145    if (NumberOfPossiblePIDsInArray <= 0)
146    {
147        //length of the array must be larger than zero.
148        return(kInvalidArgumentsError);
149    }
150
151    if (NumberOfMatchesFound == NULL) //need an integer for return.
152    {
153        return(kInvalidArgumentsError);
154    }
155   
156
157    //--- Setting return values to known values --- //
158
159    //initalizing PID array so all values are zero
160    memset(ArrayOfReturnedPIDs, 0, NumberOfPossiblePIDsInArray * sizeof(pid_t));
161       
162    *NumberOfMatchesFound = 0; //no matches found yet
163
164    if (SysctlError != NULL) //only set sysctlError if it is present
165    {
166        *SysctlError = 0;
167    }
168
169    //--- Getting list of process information for all processes --- //
170   
171    /* Setting up the mib (Management Information Base) which is an array of integers where each
172    * integer specifies how the data will be gathered.  Here we are setting the MIB
173    * block to lookup the information on all the BSD processes on the system.  Also note that
174    * every regular application has a recognized BSD process accociated with it.  We pass
175    * CTL_KERN, KERN_PROC, KERN_PROC_ALL to sysctl as the MIB to get back a BSD structure with
176    * all BSD process information for all processes in it (including BSD process names)
177    */
178    mib[0] = CTL_KERN;
179    mib[1] = KERN_PROC;
180    mib[2] = KERN_PROC_ALL;
181
182    /* Here we have a loop set up where we keep calling sysctl until we finally get an unrecoverable error
183    * (and we return) or we finally get a succesful result.  Note with how dynamic the process list can
184    * be you can expect to have a failure here and there since the process list can change between
185    * getting the size of buffer required and the actually filling that buffer.
186    */
187    SuccessfullyGotProcessInformation = FALSE;
188   
189    while (SuccessfullyGotProcessInformation == FALSE)
190    {
191        /* Now that we have the MIB for looking up process information we will pass it to sysctl to get the
192        * information we want on BSD processes.  However, before we do this we must know the size of the buffer to
193        * allocate to accomidate the return value.  We can get the size of the data to allocate also using the
194        * sysctl command.  In this case we call sysctl with the proper arguments but specify no return buffer
195        * specified (null buffer).  This is a special case which causes sysctl to return the size of buffer required.
196        *
197        * First Argument: The MIB which is really just an array of integers.  Each integer is a constant
198        *     representing what information to gather from the system.  Check out the man page to know what
199        *     constants sysctl will work with.  Here of course we pass our MIB block which was passed to us.
200        * Second Argument: The number of constants in the MIB (array of integers).  In this case there are three.
201        * Third Argument: The output buffer where the return value from sysctl will be stored.  In this case
202        *     we don't want anything return yet since we don't yet know the size of buffer needed.  Thus we will
203        *     pass null for the buffer to begin with.
204        * Forth Argument: The size of the output buffer required.  Since the buffer itself is null we can just
205        *     get the buffer size needed back from this call.
206        * Fifth Argument: The new value we want the system data to have.  Here we don't want to set any system
207        *     information we only want to gather it.  Thus, we pass null as the buffer so sysctl knows that
208        *     we have no desire to set the value.
209        * Sixth Argument: The length of the buffer containing new information (argument five).  In this case
210        *     argument five was null since we didn't want to set the system value.  Thus, the size of the buffer
211        *     is zero or NULL.
212        * Return Value: a return value indicating success or failure.  Actually, sysctl will either return
213        *     zero on no error and -1 on error.  The errno UNIX variable will be set on error.
214        */ 
215        error = sysctl(mib, 3, NULL, &sizeOfBufferRequired, NULL, 0);
216
217        /* If an error occurred then return the accociated error.  The error itself actually is stored in the UNIX
218        * errno variable.  We can access the errno value using the errno global variable.  We will return the
219        * errno value as the sysctlError return value from this function.
220        */
221        if (error != 0) 
222        {
223            if (SysctlError != NULL)
224            {
225                *SysctlError = errno;  //we only set this variable if the pre-allocated variable is given
226            } 
227
228            return(kErrorGettingSizeOfBufferRequired);
229        }
230   
231        /* Now we successful obtained the size of the buffer required for the sysctl call.  This is stored in the
232        * SizeOfBufferRequired variable.  We will malloc a buffer of that size to hold the sysctl result.
233        */
234        BSDProcessInformationStructure = (struct kinfo_proc*) malloc(sizeOfBufferRequired);
235
236        if (BSDProcessInformationStructure == NULL)
237        {
238            if (SysctlError != NULL)
239            {
240                *SysctlError = ENOMEM;  //we only set this variable if the pre-allocated variable is given
241            } 
242
243            return(kUnableToAllocateMemoryForBuffer); //unrecoverable error (no memory available) so give up
244        }
245   
246        /* Now we have the buffer of the correct size to hold the result we can now call sysctl
247        * and get the process information. 
248        *
249        * First Argument: The MIB for gathering information on running BSD processes.  The MIB is really
250        *     just an array of integers.  Each integer is a constant representing what information to
251        *     gather from the system.  Check out the man page to know what constants sysctl will work with. 
252        * Second Argument: The number of constants in the MIB (array of integers).  In this case there are three.
253        * Third Argument: The output buffer where the return value from sysctl will be stored.  This is the buffer
254        *     which we allocated specifically for this purpose. 
255        * Forth Argument: The size of the output buffer (argument three).  In this case its the size of the
256        *     buffer we already allocated. 
257        * Fifth Argument: The buffer containing the value to set the system value to.  In this case we don't
258        *     want to set any system information we only want to gather it.  Thus, we pass null as the buffer
259        *     so sysctl knows that we have no desire to set the value.
260        * Sixth Argument: The length of the buffer containing new information (argument five).  In this case
261        *     argument five was null since we didn't want to set the system value.  Thus, the size of the buffer
262        *     is zero or NULL.
263        * Return Value: a return value indicating success or failure.  Actually, sysctl will either return
264        *     zero on no error and -1 on error.  The errno UNIX variable will be set on error.
265        */ 
266        error = sysctl(mib, 3, BSDProcessInformationStructure, &sizeOfBufferRequired, NULL, 0);
267   
268        //Here we successfully got the process information.  Thus set the variable to end this sysctl calling loop
269        if (error == 0)
270        {
271            SuccessfullyGotProcessInformation = TRUE;
272        }
273        else 
274        {
275            /* failed getting process information we will try again next time around the loop.  Note this is caused
276            * by the fact the process list changed between getting the size of the buffer and actually filling
277            * the buffer (something which will happen from time to time since the process list is dynamic).
278            * Anyways, the attempted sysctl call failed.  We will now begin again by freeing up the allocated
279            * buffer and starting again at the beginning of the loop.
280            */
281            free(BSDProcessInformationStructure); 
282        }
283    }//end while loop
284
285    // --- Going through process list looking for processes with matching names --- //
286
287    /* Now that we have the BSD structure describing the running processes we will parse it for the desired
288     * process name.  First we will the number of running processes.  We can determine
289     * the number of processes running because there is a kinfo_proc structure for each process.
290     */
291    NumberOfRunningProcesses = sizeOfBufferRequired / sizeof(struct kinfo_proc); 
292   
293    /* Now we will go through each process description checking to see if the process name matches that
294     * passed to us.  The BSDProcessInformationStructure has an array of kinfo_procs.  Each kinfo_proc has
295     * an extern_proc accociated with it in the kp_proc attribute.  Each extern_proc (kp_proc) has the process name
296     * of the process accociated with it in the p_comm attribute and the PID of that process in the p_pid attibute.
297     * We test the process name by compairing the process name passed to us with the value in the p_comm value.
298     * Note we limit the compairison to MAXCOMLEN which is the maximum length of a BSD process name which is used
299     * by the system.
300     */
301    for (Counter = 0 ; Counter < NumberOfRunningProcesses ; Counter++)
302    {
303        //Getting PID of process we are examining
304        CurrentExaminedProcessPID = BSDProcessInformationStructure[Counter].kp_proc.p_pid; 
305   
306        //Getting name of process we are examining
307        CurrentExaminedProcessName = BSDProcessInformationStructure[Counter].kp_proc.p_comm; 
308       
309        if ((CurrentExaminedProcessPID > 0) //Valid PID
310           && ((strncmp(CurrentExaminedProcessName, ProcessName, MAXCOMLEN) == 0))) //name matches
311        {       
312            // --- Got a match add it to the array if possible --- //
313            if ((*NumberOfMatchesFound + 1) > NumberOfPossiblePIDsInArray)
314            {
315                //if we overran the array buffer passed we release the allocated buffer give an error.
316                free(BSDProcessInformationStructure);
317                return(kPIDBufferOverrunError);
318            }
319       
320            //adding the value to the array.
321            ArrayOfReturnedPIDs[*NumberOfMatchesFound] = CurrentExaminedProcessPID;
322           
323            //incrementing our number of matches found.
324            *NumberOfMatchesFound = *NumberOfMatchesFound + 1;
325        }
326    }//end looking through process list
327
328    free(BSDProcessInformationStructure); //done with allocated buffer so release.
329
330    if (*NumberOfMatchesFound == 0)
331    {
332        //didn't find any matches return error.
333        return(kCouldNotFindRequestedProcess);
334    }
335    else
336    {
337        //found matches return success.
338        return(kSuccess);
339    }
340}
341
342/*****************************************************
343 * GetPIDForProcessName
344 *****************************************************
345 * Purpose:  A convience call for GetAllPIDsForProcessName().
346 * This function looks up a process PID given a BSD process
347 * name. 
348 *
349 * Parameters:
350 *      ProcessName             A constant C-string.  On calling
351 * GetPIDForProcessName this variable holds the BSD process name
352 * used to do the process lookup.  Note that the process name you need
353 * to pass is the name of the BSD executable process.  If trying
354 * to find the PID of an regular OSX application you will need to pass the
355 * name of the actual BSD executable inside an application bundle (rather
356 * than the bundle name itself).  In any case as a user you can find the
357 * BSD process name of any process (including OSX applications) by
358 * typing the command "ps -axcocommand,pid" in terminal.
359 *
360 *      *Function Result*       A integer return value.
361 *                              See result codes listed below.
362 *              Result Codes:
363 *                >0            Success.  The value returned is the PID of the
364 *                              matching process.
365 *               -1             Error getting PID for requested process.  This error can
366 *                              be caused by several things.  One is if no such process exists.
367 *                              Another is if more than one process has the given name.  The
368 *                              thing to do here is to call GetAllPIDsForProcessName()
369 *                              for complete error code or to get PIDs if there are multiple
370 *                              processes with that name.
371 *****************************************************/
372int GetPIDForProcessName(const char* ProcessName)
373{
374    pid_t PIDArray[1] = {0};
375    int Error = 0;
376    unsigned int NumberOfMatches = 0; 
377
378   /* Here we are calling the function GetAllPIDsForProcessName which wil give us the PIDs
379    * of the process name we pass.  Of course here we are hoping for a single PID return.
380    * First Argument: The BSD process name of the process we want to lookup.  In this case the
381    *   the process name passed to us.
382    * Second Argument: A preallocated array of pid_t.  This is where the PIDs of matching processes
383    *   will be placed on return.  We pass the array we just allocated which is length one.
384    * Third Argument: The number of pid_t entries located in the array of pid_t (argument 2).  In this
385    *   case our array has one pid_t entry so pass one.
386    * Forth Argument: On return this will hold the number of PIDs placed into the
387    *   pid_t array (array passed in argument 2).
388    * Fifth Argument: Passing NULL to ignore this argument.
389    * Return Value: An error indicating success (zero result) or failure (non-zero).
390    *   
391    */
392    Error = GetAllPIDsForProcessName(ProcessName, PIDArray, 1, &NumberOfMatches, NULL);
393   
394    if ((Error == 0) && (NumberOfMatches == 1))//success! 
395    {
396        return((int) PIDArray[0]); //return the one PID we found.
397    }
398    else 
399    {
400        return(-1);
401    }
402}
Note: See TracBrowser for help on using the repository browser.