1
/*********************************************************
2
* Copyright (C) 2010 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License as published
6
* by the Free Software Foundation version 2.1 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
11
* License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17
*********************************************************/
23
* Solaris specific implementations of the process management lib methods.
28
#include <sys/types.h>
34
#include <sys/param.h>
50
ReadArgsFromAddressSpaceFile(FileIODescriptor asFd,
52
DynBufArray *cmdLineArr);
55
ReadOffsetsFromAddressSpaceFile(FileIODescriptor asFd,
60
ExtractArgStringFromAddressSpaceFile(FileIODescriptor asFd,
64
ExtractCommandLineFromAddressSpaceFile(psinfo_t *procInfo);
67
*----------------------------------------------------------------------------
69
* ProcMgr_ListProcesses --
71
* List all the processes that the calling client has privilege to
72
* enumerate. The strings in the returned structure should be all
73
* UTF-8 encoded, although we do not enforce it right now.
77
* On success, the process list is returned and the caller is responsible
78
* for freeing the memory used by calling ProcMgr_FreeProcList. On
79
* failure, NULL is returned.
85
*----------------------------------------------------------------------------
89
ProcMgr_ListProcesses(void)
91
ProcMgr_ProcList *procList = NULL;
96
DynBuf dbProcStartTime;
101
DynBuf_Init(&dbProcId);
102
DynBuf_Init(&dbProcCmd);
103
DynBuf_Init(&dbProcStartTime);
104
DynBuf_Init(&dbProcOwner);
106
dir = opendir("/proc");
108
Warning("ProcMgr_ListProcesses unable to open /proc\n");
117
char *userName = NULL;
118
char tempPath[MAXPATHLEN];
119
time_t processStartTime;
123
FileIODescriptor psInfoFd;
127
FileIO_Invalidate(&psInfoFd);
139
if (Str_Snprintf(tempPath,
142
ent->d_name) == -1) {
143
Debug("Process id '%s' too large\n", ent->d_name);
146
res = FileIO_Open(&psInfoFd,
148
FILEIO_OPEN_ACCESS_READ,
150
if (res != FILEIO_SUCCESS) {
151
if ((res == FILEIO_FILE_NOT_FOUND) ||
152
(res == FILEIO_NO_PERMISSION)) {
160
res = FileIO_Read(&psInfoFd, &procInfo, sizeof procInfo, &numRead);
161
FileIO_Close(&psInfoFd);
162
if (res != FILEIO_SUCCESS) {
167
processStartTime = procInfo.pr_start.tv_sec;
170
* Command line strings in procInfo.pr_psargs are truncated to PRARGZ
171
* bytes. In this case we extract the arguments from the /proc/<pid>/as
172
* file. Since most command line strings are expected to fit within
173
* PRARGSZ bytes, we avoid calling
174
* ExtractCommandLineFromAddressSpaceFile for every process.
176
if (strlen(procInfo.pr_psargs) + 1 == PRARGSZ) {
179
tmp = ExtractCommandLineFromAddressSpaceFile(&procInfo);
181
cmdLineTemp = Unicode_Alloc(tmp, STRING_ENCODING_DEFAULT);
184
cmdLineTemp = Unicode_Alloc(procInfo.pr_psargs,
185
STRING_ENCODING_DEFAULT);
188
cmdLineTemp = Unicode_Alloc(procInfo.pr_psargs,
189
STRING_ENCODING_DEFAULT);
193
* Store the command line string pointer in dynbuf.
195
DynBuf_Append(&dbProcCmd, &cmdLineTemp, sizeof cmdLineTemp);
198
* Store the pid in dynbuf.
200
pid = procInfo.pr_pid;
201
DynBuf_Append(&dbProcId, &pid, sizeof pid);
204
* Store the owner of the process.
206
pwd = getpwuid(procInfo.pr_uid);
207
userName = (NULL == pwd)
208
? Str_Asprintf(&strLen, "%d", (int) procInfo.pr_uid)
209
: Unicode_Alloc(pwd->pw_name, STRING_ENCODING_DEFAULT);
210
DynBuf_Append(&dbProcOwner, &userName, sizeof userName);
213
* Store the time that the process started.
215
DynBuf_Append(&dbProcStartTime,
217
sizeof processStartTime);
222
if (0 == DynBuf_GetSize(&dbProcId)) {
228
* We're done adding to DynBuf. Trim off any unused allocated space.
229
* DynBuf_Trim() followed by DynBuf_Detach() avoids a memcpy().
231
DynBuf_Trim(&dbProcId);
232
DynBuf_Trim(&dbProcCmd);
233
DynBuf_Trim(&dbProcStartTime);
234
DynBuf_Trim(&dbProcOwner);
237
* Create a ProcMgr_ProcList and populate its fields.
239
procList = (ProcMgr_ProcList *) Util_SafeCalloc(1, sizeof(ProcMgr_ProcList));
240
ASSERT_MEM_ALLOC(procList);
242
procList->procCount = DynBuf_GetSize(&dbProcId) / sizeof(pid_t);
244
procList->procIdList = (pid_t *) DynBuf_Detach(&dbProcId);
245
ASSERT_MEM_ALLOC(procList->procIdList);
246
procList->procCmdList = (char **) DynBuf_Detach(&dbProcCmd);
247
ASSERT_MEM_ALLOC(procList->procCmdList);
248
procList->startTime = (time_t *) DynBuf_Detach(&dbProcStartTime);
249
ASSERT_MEM_ALLOC(procList->startTime);
250
procList->procOwnerList = (char **) DynBuf_Detach(&dbProcOwner);
251
ASSERT_MEM_ALLOC(procList->procOwnerList);
254
DynBuf_Destroy(&dbProcId);
255
DynBuf_Destroy(&dbProcCmd);
256
DynBuf_Destroy(&dbProcStartTime);
257
DynBuf_Destroy(&dbProcOwner);
260
ProcMgr_FreeProcList(procList);
269
*----------------------------------------------------------------------------
271
* ExtractCommandLineFromAddressSpaceFile --
273
* Read the address space file (/proc/<pid>/as) for a given process and
274
* return its command line string.
278
* On success, the command line string for the process is returned and the
279
* caller is responsible for freeing the memory used by this string. On
280
* failure, NULL is returned.
286
*----------------------------------------------------------------------------
290
ExtractCommandLineFromAddressSpaceFile(psinfo_t *procInfo) //IN: psinfo struct
294
char tempPath[MAXPATHLEN];
296
FileIODescriptor asFd;
302
FileIO_Invalidate(&asFd);
303
pid = procInfo->pr_pid;
305
if (Str_Snprintf(tempPath,
307
"/proc/%"FMT64"d/as",
308
(int64_t)pid) == -1) {
309
/* This should not happen. MAXPATHLEN should be large enough. */
312
res = FileIO_Open(&asFd,
314
FILEIO_OPEN_ACCESS_READ,
316
if (res != FILEIO_SUCCESS) {
317
Warning("Could not open address space file for pid %"FMT64"d, %s\n",
319
FileIO_MsgError(res));
324
if (ReadArgsFromAddressSpaceFile(asFd, procInfo, &args)) {
325
/* Concatenate the strings in args into cmdLine. */
326
DynBuf_Init(&cmdLine);
327
argc = DynBufArray_Count(&args);
328
for (i = 0; i < argc; i++) {
329
buf = DynBuf_Get(DynBufArray_AddressOf(&args, i));
330
DynBuf_Append(&cmdLine, buf, strlen(buf));
332
DynBuf_Append(&cmdLine, " ", 1);
334
DynBuf_Destroy(DynBufArray_AddressOf(&args, i));
336
DynBuf_AppendString(&cmdLine,"");
337
DynBufArray_Destroy(&args);
338
DynBuf_Trim(&cmdLine);
339
buf = DynBuf_Detach(&cmdLine);
340
DynBuf_Destroy(&cmdLine);
348
*----------------------------------------------------------------------------
350
* ReadArgsFromAddressSpaceFile --
352
* Read the command line arguments for a process and store them in the
353
* cmdLineArr array. The processes' address space file must be open with
354
* the file descriptor adFd. This function assumes that it runs in the
355
* same locale as the process being inspected.
359
* On success, TRUE is returned and the caller is responsible for
360
* de-allocating the memory used by the DynBufArray; by calling
361
* DynBuf_Destroy on each of its elements, and then DynBufArray_Destroy on
362
* the array itself. FALSE is returned on failure, and no de-allocation
367
* The cmdLineArr array is filled with the command line strings of the
369
*----------------------------------------------------------------------------
373
ReadArgsFromAddressSpaceFile(FileIODescriptor asFd, //IN
374
psinfo_t *psInfo, //IN
375
DynBufArray *cmdLineArr) //OUT
379
uintptr_t nextArgOff;
386
argc = psInfo->pr_argc;
387
DynBufArray_Init(cmdLineArr, argc);
388
for (i = 0; i < argc; i++) {
389
DynBuf_Init(DynBufArray_AddressOf(cmdLineArr, i));
394
argOffs = Util_SafeCalloc(argc, sizeof *argOffs);
395
if (argOffs == NULL) {
399
if (!ReadOffsetsFromAddressSpaceFile(asFd, psInfo, argOffs)) {
403
/* Read the command line arguments into the cmdLineArr array. */
404
nextArgOff = argc > 0 ? argOffs[0] : 0;
410
* The argument strings are contiguous in the address space file. So
411
* argOff[i] + strlen(arg[i]) + 1 should be equal to argOff[i + 1].
413
if ((argOff == 0) || (argOff != nextArgOff)) {
416
argBuf = ExtractArgStringFromAddressSpaceFile(asFd, argOff);
417
if (argBuf == NULL) {
420
nextArgOff = argOff + strlen(argBuf) + 1;
421
argBufPtr = argBuf + strlen(argBuf);
422
while ((argBufPtr > argBuf) && isspace(*(argBufPtr - 1))) {
426
arg = DynBufArray_AddressOf(cmdLineArr, i);
427
if (!DynBuf_Append(arg,
429
strlen(argBuf) + 1)) {
439
Warning("Failed to read command line arguments\n");
440
argc = DynBufArray_Count(cmdLineArr);
441
for (i = 0; i < argc; i++) {
442
arg = DynArray_AddressOf(cmdLineArr, i);
445
DynBufArray_SetCount(cmdLineArr, 0);
446
DynBufArray_Destroy(cmdLineArr);
453
*----------------------------------------------------------------------------
455
* ReadOffsetsFromAddressSpaceFile --
457
* Read the offsets for the command line arguments strings of a process
458
* into the argOffs array. The processes' /proc/<pid>/as file must be
459
* open with the file descriptor asFd. The argOffs array must have enough
460
* space to store all the offsets for the process.
464
* TRUE on success, FALSE on error.
468
* The argOffs array is filled with the offsets of the command line
471
*----------------------------------------------------------------------------
475
ReadOffsetsFromAddressSpaceFile(FileIODescriptor asFd, //IN
476
psinfo_t *psInfo, //IN
477
uintptr_t *argOffs) //OUT
484
argc = psInfo->pr_argc;
485
argv = psInfo->pr_argv;
487
* The offsets for the command line argument are located at an offset of
488
* argv in the /proc/<pid>/as file. If the process data model is NATIVE,
489
* each offset is a unitptr_t; else if the data model is ILP32 or LP64, each
490
* offset is uint32_t.
492
if (psInfo->pr_dmodel == PR_MODEL_NATIVE) {
494
* The offset for each arguments is sizeof uintptr_t bytes.
496
res = FileIO_Pread(&asFd, argOffs, argc * sizeof argv, argv);
497
if (res != FILEIO_SUCCESS) {
502
* The offset for each arguments is sizeof uint32_t bytes.
505
argOffs32 = Util_SafeCalloc(argc, sizeof *argOffs32);
506
if (argOffs32 == NULL) {
509
res = FileIO_Pread(&asFd, argOffs32, argc * sizeof *argOffs32, argv);
510
if (res != FILEIO_SUCCESS) {
514
for (i = 0; i < argc; i++) {
515
argOffs[i] = argOffs32[i];
524
*----------------------------------------------------------------------------
526
* ExtractArgStringFromAddressSpaceFile --
528
* Extract a string at a given offset in the given file. The file must be
529
* open with file descriptor asFd.
533
* On success, the NULL terminated string is returned and the
534
* caller is responsible for freeing the memory used by this string. On
535
* failure, NULL is returned.
541
*----------------------------------------------------------------------------
545
ExtractArgStringFromAddressSpaceFile(FileIODescriptor asFd, //IN
546
uintptr_t offset) //IN
552
buf = Util_SafeMalloc(readSize * sizeof *buf);
554
res = FileIO_Pread(&asFd, buf, readSize, offset);
555
if (res != FILEIO_SUCCESS) {
558
if (Str_Strlen(buf, readSize) == readSize) {
561
if (readSize > NCARGS) {
564
buf = Util_SafeMalloc(readSize * sizeof *buf);
578
*----------------------------------------------------------------------
580
* ProcMgr_ImpersonateUserStart --
582
* Impersonate a user. Much like bora/lib/impersonate, but
583
* changes the real and saved uid as well, to work with syscalls
584
* (access() and kill()) that look at real UID instead of effective.
585
* The user name should be UTF-8 encoded, although we do not enforce
586
* it right now. Solaris does not have setresuid/setresgid. So perform
587
* a two step process to set the real and effective uid/gid to given
588
* user and leave the saved uid/gid as root.
590
* Assumes it will be called as root.
593
* TRUE on success, FALSE on failure
597
* Uid/gid set to given user, saved uid/gid left as root.
599
*----------------------------------------------------------------------
603
ProcMgr_ImpersonateUserStart(const char *user, // IN: UTF-8 encoded user name
604
AuthToken token) // IN
613
if ((ppw = getpwuid_r(0, &pw, buffer, sizeof buffer)) == NULL) {
617
root_gid = ppw->pw_gid;
619
if ((ppw = getpwnam_r(user, &pw, buffer, sizeof buffer)) == NULL) {
623
/* first change group. */
624
ret = Id_SetGid(root_gid);
626
Warning("Failed to setregid() for root\n");
630
/* Snippet from Solaris setregid man page --
632
* A -1 argument does not change the corresponding gid. If the real user ID
633
* is being changed, or the effective user ID is being changed to a value
634
* not equal to the real user ID, the saved set-user ID is set equal to
635
* the new effective user ID.
637
ret = Id_SetREGid(ppw->pw_gid, -1);
639
Warning("Failed to setregid() for user %s\n", user);
642
ret = Id_SetREGid(-1, ppw->pw_gid);
644
Warning("Failed to setregid() for user %s\n", user);
647
ret = initgroups(ppw->pw_name, ppw->pw_gid);
649
Warning("Failed to initgroups() for user %s\n", user);
656
Warning("Failed to setregid() for root\n");
661
ret = Id_SetREUid(ppw->pw_uid, -1);
663
Warning("Failed to setreuid() for user %s\n", user);
666
ret = Id_SetREUid(-1, ppw->pw_uid);
668
Warning("Failed to setreuid() for user %s\n", user);
673
setenv("USER", ppw->pw_name, 1);
674
setenv("HOME", ppw->pw_dir, 1);
675
setenv("SHELL", ppw->pw_shell, 1);
680
/* try to restore on error. */
681
ProcMgr_ImpersonateUserStop();
687
*----------------------------------------------------------------------
689
* ProcMgr_ImpersonateUserStop --
691
* Stop impersonating a user and return to root. Solaris does not
692
* have setresuid/setresgid. So perform a two step process while
693
* restoring uids to root.
696
* TRUE on success, FALSE on failure.
700
* Uid/gid restored to root.
702
*----------------------------------------------------------------------
706
ProcMgr_ImpersonateUserStop(void)
714
if ((ppw = getpwuid_r(0, &pw, buffer, sizeof buffer)) == NULL) {
718
/* first change back user, Do the same two step process as above. */
719
ret = Id_SetREUid(-1, ppw->pw_uid);
721
Warning("Failed setreuid() for root\n");
724
ret = Id_SetREUid(ppw->pw_uid, -1);
726
Warning("Failed to setreuid() for root\n");
731
ret = Id_SetGid(ppw->pw_gid);
733
Warning("Failed to setgid() for root\n");
736
ret = initgroups(ppw->pw_name, ppw->pw_gid);
738
Warning("Failed to initgroups() for root\n");
743
setenv("USER", ppw->pw_name, 1);
744
setenv("HOME", ppw->pw_dir, 1);
745
setenv("SHELL", ppw->pw_shell, 1);
752
*----------------------------------------------------------------------
754
* ProcMgr_GetImpersonatedUserInfo --
756
* Return info about the impersonated user.
759
* TRUE on success, FALSE on failure.
763
*----------------------------------------------------------------------
767
ProcMgr_GetImpersonatedUserInfo(char **userName, // OUT
768
char **homeDir) // OUT
778
if ((ppw = getpwuid_r(Id_GetEUid(), &pw, buffer, sizeof buffer)) == NULL) {
782
*userName = Unicode_Alloc(ppw->pw_name, STRING_ENCODING_DEFAULT);
783
*homeDir = Unicode_Alloc(ppw->pw_dir, STRING_ENCODING_DEFAULT);