| /******************************************************************************* |
| * Copyright (c) 2007, 2017 Wind River Systems, Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * and Eclipse Distribution License v1.0 which accompany this distribution. |
| * The Eclipse Public License is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * You may elect to redistribute this code under either of these licenses. |
| * |
| * Contributors: |
| * Wind River Systems - initial API and implementation |
| *******************************************************************************/ |
| |
| #include <tcf/config.h> |
| |
| #if SERVICE_SysMonitor |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <assert.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <tcf/framework/mdep-fs.h> |
| #include <tcf/framework/protocol.h> |
| #include <tcf/framework/exceptions.h> |
| #include <tcf/framework/myalloc.h> |
| #include <tcf/framework/json.h> |
| #include <tcf/framework/context.h> |
| #include <tcf/framework/errors.h> |
| #include <tcf/services/sysmon.h> |
| |
| static const char SYS_MON[] = "SysMonitor"; |
| |
| #if defined(_WRS_KERNEL) |
| |
| # error "SysMonitor service is not supported for VxWorks" |
| |
| #elif defined(__FreeBSD__) || defined(__NetBSD__) |
| |
| # error "SysMonitor service is not supported for BSD" |
| |
| #elif defined(__APPLE__) |
| |
| #include <unistd.h> |
| #include <pwd.h> |
| #include <grp.h> |
| #include <stdbool.h> |
| #include <sys/sysctl.h> |
| #include <mach/mach.h> |
| #include <mach/task_info.h> |
| #include <mach/thread_info.h> |
| |
| typedef struct kinfo_proc kinfo_proc; |
| |
| static void write_string_array(OutputStream * out, char **ap, int len) { |
| int cnt; |
| |
| write_stream(out, '['); |
| for (cnt = 0; cnt < len; cnt++) { |
| if (cnt > 0) write_stream(out, ','); |
| json_write_string(out, ap[cnt]); |
| } |
| write_stream(out, ']'); |
| } |
| |
| static void free_array(char **ap, int len) { |
| int c; |
| for (c = 0; c < len; c++) { |
| free(*ap++); |
| } |
| free(ap); |
| } |
| |
| /* |
| * Get kernel process information for all processes. |
| */ |
| static int get_allprocesses(kinfo_proc **kprocs, int *nprocs) |
| { |
| size_t len; |
| kinfo_proc * kp; |
| int mib_name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; |
| int mib_len = 3; |
| |
| if (sysctl(mib_name, mib_len, NULL, &len, NULL, 0) < 0) { |
| return -1; |
| } |
| |
| kp = (struct kinfo_proc *)malloc(len); |
| |
| if (sysctl(mib_name, mib_len, kp, &len, NULL, 0) < 0) { |
| free(kp); |
| return -1; |
| } |
| |
| *kprocs = kp; |
| *nprocs = len / sizeof(kinfo_proc); |
| return 0; |
| } |
| |
| /* |
| * Get kernel process information for a specified pid. |
| */ |
| static kinfo_proc *get_process(pid_t pid) |
| { |
| kinfo_proc * kp; |
| int mib_name[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, 0}; |
| int mib_len = 4; |
| size_t len = sizeof(kinfo_proc); |
| |
| mib_name[3] = pid; |
| |
| kp = malloc(len); |
| if (kp == NULL) { |
| return NULL; |
| } |
| |
| if (sysctl(mib_name, mib_len, kp, &len, NULL, 0) < 0) { |
| free(kp); |
| return NULL; |
| } |
| |
| return kp; |
| } |
| |
| static void write_context(OutputStream * out, char * id, char * parent_id, kinfo_proc * p) { |
| struct passwd * pwd; |
| struct group * grp; |
| |
| write_stream(out, '{'); |
| |
| json_write_string(out, "UID"); |
| write_stream(out, ':'); |
| json_write_long(out, p->kp_eproc.e_ucred.cr_uid); |
| write_stream(out, ','); |
| |
| json_write_string(out, "UGID"); |
| write_stream(out, ':'); |
| json_write_long(out, p->kp_eproc.e_pcred.p_rgid); |
| write_stream(out, ','); |
| |
| pwd = getpwuid(p->kp_eproc.e_ucred.cr_uid); |
| if (pwd != NULL) { |
| json_write_string(out, "UserName"); |
| write_stream(out, ':'); |
| json_write_string(out, pwd->pw_name); |
| write_stream(out, ','); |
| } |
| |
| grp = getgrgid(p->kp_eproc.e_pcred.p_rgid); |
| if (grp != NULL) { |
| json_write_string(out, "GroupName"); |
| write_stream(out, ':'); |
| json_write_string(out, grp->gr_name); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "File"); |
| write_stream(out, ':'); |
| json_write_string(out, p->kp_proc.p_comm); |
| write_stream(out, ','); |
| |
| json_write_string(out, "PID"); |
| write_stream(out, ':'); |
| json_write_long(out, p->kp_proc.p_pid); |
| write_stream(out, ','); |
| |
| json_write_string(out, "State"); |
| write_stream(out, ':'); |
| write_stream(out, '"'); |
| json_write_char(out, p->kp_proc.p_stat); |
| write_stream(out, '"'); |
| write_stream(out, ','); |
| |
| if (p->kp_eproc.e_ppid > 0) { |
| json_write_string(out, "PPID"); |
| write_stream(out, ':'); |
| json_write_long(out, p->kp_eproc.e_ppid); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "PGRP"); |
| write_stream(out, ':'); |
| json_write_long(out, p->kp_eproc.e_pgid); |
| write_stream(out, ','); |
| |
| if (p->kp_eproc.e_tpgid > 0) { |
| json_write_string(out, "TGID"); |
| write_stream(out, ':'); |
| json_write_long(out, p->kp_eproc.e_tpgid); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "Flags"); |
| write_stream(out, ':'); |
| json_write_long(out, p->kp_proc.p_flag); |
| write_stream(out, ','); |
| |
| json_write_string(out, "UTime"); |
| write_stream(out, ':'); |
| json_write_uint64(out, p->kp_proc.p_uticks); |
| write_stream(out, ','); |
| |
| json_write_string(out, "STime"); |
| write_stream(out, ':'); |
| json_write_uint64(out, p->kp_proc.p_sticks); |
| write_stream(out, ','); |
| |
| json_write_string(out, "Priority"); |
| write_stream(out, ':'); |
| json_write_long(out, (long)p->kp_proc.p_priority); |
| write_stream(out, ','); |
| |
| if (p->kp_proc.p_nice != 0) { |
| json_write_string(out, "Nice"); |
| write_stream(out, ':'); |
| json_write_long(out, (long)p->kp_proc.p_nice); |
| write_stream(out, ','); |
| } |
| |
| if (parent_id != NULL && parent_id[0] != 0) { |
| json_write_string(out, "ParentID"); |
| write_stream(out, ':'); |
| json_write_string(out, parent_id); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "ID"); |
| write_stream(out, ':'); |
| json_write_string(out, id); |
| |
| write_stream(out, '}'); |
| } |
| |
| static void command_get_context(char * token, Channel * c) { |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| int err = 0; |
| kinfo_proc * p; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| pid = id2pid(id, &parent); |
| p = get_process(pid); |
| if (p == NULL) err = errno; |
| |
| write_errno(&c->out, err); |
| |
| if (err == 0 && pid != 0) { |
| char *parent_id; |
| asprintf(&parent_id, "%d", parent); |
| write_context(&c->out, id, parent == 0 ? NULL : parent_id, p); |
| write_stream(&c->out, 0); |
| free(parent_id); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_children(char * token, Channel * c) { |
| int err = 0; |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| pid = id2pid(id, &parent); |
| |
| if (parent != 0) { |
| write_errno(&c->out, 0); |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| if (pid == 0) { |
| int np; |
| int i; |
| int n; |
| kinfo_proc * p; |
| |
| if (get_allprocesses(&p, &np) < 0) { |
| write_errno(&c->out, errno); |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| write_errno(&c->out, 0); |
| write_stream(&c->out, '['); |
| for (n = 0, i = 0; i < np; i++) { |
| if (p->kp_proc.p_pid != 0) { |
| if (n > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, pid2id(p->kp_proc.p_pid, 0)); |
| n++; |
| } |
| p++; |
| } |
| write_stream(&c->out, ']'); |
| write_stream(&c->out, 0); |
| } |
| } |
| else { |
| kinfo_proc * p; |
| |
| p = get_process(pid); |
| if (p == NULL) { |
| write_errno(&c->out, errno); |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| task_port_t task; |
| |
| if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS) { |
| /* |
| * User is probably not in procmod group |
| */ |
| write_errno(&c->out, 0); |
| write_stringz(&c->out, "[]"); |
| } |
| else { |
| unsigned int thread_count; |
| thread_port_array_t thread_list; |
| |
| if (task_threads(task, &thread_list, &thread_count) != KERN_SUCCESS) { |
| write_errno(&c->out, errno); |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| int cnt; |
| write_errno(&c->out, 0); |
| write_stream(&c->out, '['); |
| for (cnt = 0; cnt < thread_count; cnt++) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, pid2id(thread_list[cnt], pid)); |
| } |
| write_stream(&c->out, ']'); |
| write_stream(&c->out, 0); |
| } |
| } |
| } |
| } |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_command_line(char * token, Channel * c) { |
| int err; |
| char id[256]; |
| pid_t pid; |
| pid_t parent; |
| kinfo_proc * p; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| pid = id2pid(id, &parent); |
| if (pid != 0 && parent == 0) { |
| p = get_process(pid); |
| if (p == NULL) err = errno; |
| } |
| else { |
| err = ERR_INV_CONTEXT; |
| } |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| write_errno(&c->out, err); |
| |
| if (err != 0) { |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| write_stringz(&c->out, p->kp_proc.p_comm); |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_environment(char * token, Channel * c) { |
| char id[256]; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| write_errno(&c->out, 0); |
| write_stringz(&c->out, "[]"); |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| #elif defined(_WIN32) || defined(__CYGWIN__) |
| |
| #include <windows.h> |
| #include <wchar.h> |
| |
| typedef struct _UNICODE_STRING { |
| USHORT Length; |
| USHORT MaximumLength; |
| PWSTR Buffer; |
| } UNICODE_STRING; |
| |
| typedef enum _PROCESSINFOCLASS { |
| ProcessBasicInformation = 0, |
| ProcessWow64Information = 26 |
| } PROCESSINFOCLASS; |
| |
| typedef struct _RTL_DRIVE_LETTER_CURDIR { |
| USHORT Flags; |
| USHORT Length; |
| ULONG TimeStamp; |
| UNICODE_STRING DosPath; |
| } RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; |
| |
| typedef struct _RTL_USER_PROCESS_PARAMETERS { |
| ULONG MaximumLength; |
| ULONG Length; |
| ULONG Flags; |
| ULONG DebugFlags; |
| PVOID ConsoleHandle; |
| ULONG ConsoleFlags; |
| HANDLE StdInputHandle; |
| HANDLE StdOutputHandle; |
| HANDLE StdErrorHandle; |
| UNICODE_STRING CurrentDirectoryPath; |
| HANDLE CurrentDirectoryHandle; |
| UNICODE_STRING DllPath; |
| UNICODE_STRING ImagePathName; |
| UNICODE_STRING CommandLine; |
| PVOID Environment; |
| ULONG StartingPositionLeft; |
| ULONG StartingPositionTop; |
| ULONG Width; |
| ULONG Height; |
| ULONG CharWidth; |
| ULONG CharHeight; |
| ULONG ConsoleTextAttributes; |
| ULONG WindowFlags; |
| ULONG ShowWindowFlags; |
| UNICODE_STRING WindowTitle; |
| UNICODE_STRING DesktopName; |
| UNICODE_STRING ShellInfo; |
| UNICODE_STRING RuntimeData; |
| RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; |
| } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; |
| |
| typedef struct _PEB_LDR_DATA * PPEB_LDR_DATA; |
| typedef struct _PEBLOCKROUTINE * PPEBLOCKROUTINE; |
| typedef struct _PEB_FREE_BLOCK * PPEB_FREE_BLOCK; |
| typedef PVOID * PPVOID; |
| |
| typedef struct _PROCESS_ENVIRONMENT_BLOCK { |
| BOOLEAN InheritedAddressSpace; |
| BOOLEAN ReadImageFileExecOptions; |
| BOOLEAN BeingDebugged; |
| BOOLEAN Spare; |
| HANDLE Mutant; |
| PVOID ImageBaseAddress; |
| PPEB_LDR_DATA LoaderData; |
| PRTL_USER_PROCESS_PARAMETERS ProcessParameters; |
| PVOID SubSystemData; |
| PVOID ProcessHeap; |
| PVOID FastPebLock; |
| PPEBLOCKROUTINE FastPebLockRoutine; |
| PPEBLOCKROUTINE FastPebUnlockRoutine; |
| ULONG EnvironmentUpdateCount; |
| PPVOID KernelCallbackTable; |
| PVOID EventLogSection; |
| PVOID EventLog; |
| PPEB_FREE_BLOCK FreeList; |
| ULONG TlsExpansionCounter; |
| PVOID TlsBitmap; |
| ULONG TlsBitmapBits[0x2]; |
| PVOID ReadOnlySharedMemoryBase; |
| PVOID ReadOnlySharedMemoryHeap; |
| PPVOID ReadOnlyStaticServerData; |
| PVOID AnsiCodePageData; |
| PVOID OemCodePageData; |
| PVOID UnicodeCaseTableData; |
| ULONG NumberOfProcessors; |
| ULONG NtGlobalFlag; |
| BYTE Spare2[0x4]; |
| LARGE_INTEGER CriticalSectionTimeout; |
| ULONG HeapSegmentReserve; |
| ULONG HeapSegmentCommit; |
| ULONG HeapDeCommitTotalFreeThreshold; |
| ULONG HeapDeCommitFreeBlockThreshold; |
| ULONG NumberOfHeaps; |
| ULONG MaximumNumberOfHeaps; |
| PPVOID *ProcessHeaps; |
| PVOID GdiSharedHandleTable; |
| PVOID ProcessStarterHelper; |
| PVOID GdiDCAttributeList; |
| PVOID LoaderLock; |
| ULONG OSMajorVersion; |
| ULONG OSMinorVersion; |
| ULONG OSBuildNumber; |
| ULONG OSPlatformId; |
| ULONG ImageSubSystem; |
| ULONG ImageSubSystemMajorVersion; |
| ULONG ImageSubSystemMinorVersion; |
| ULONG GdiHandleBuffer[0x22]; |
| ULONG PostProcessInitRoutine; |
| ULONG TlsExpansionBitmap; |
| BYTE TlsExpansionBitmapBits[0x80]; |
| ULONG SessionId; |
| } loc_PEB, *loc_PPEB; |
| |
| typedef struct loc_PROCESS_BASIC_INFORMATION { |
| LONG ExitStatus; |
| loc_PPEB PebBaseAddress; |
| ULONG_PTR AffinityMask; |
| LONG BasePriority; |
| ULONG_PTR UniqueProcessId; |
| ULONG_PTR InheritedFromUniqueProcessId; |
| } PBI; |
| |
| static PBI pbi; |
| static loc_PEB peb; |
| static RTL_USER_PROCESS_PARAMETERS upa; |
| |
| static int get_process_info(HANDLE prs) { |
| typedef LONG (NTAPI * QueryInformationProcessProcType)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); |
| static QueryInformationProcessProcType QueryInformationProcessProc; |
| |
| memset(&pbi, 0, sizeof(pbi)); |
| memset(&peb, 0, sizeof(peb)); |
| memset(&upa, 0, sizeof(upa)); |
| |
| if (QueryInformationProcessProc == NULL) { |
| QueryInformationProcessProc = (QueryInformationProcessProcType)GetProcAddress( |
| GetModuleHandle("NTDLL.DLL"), "ZwQueryInformationProcess"); |
| if (QueryInformationProcessProc == NULL) { |
| set_win32_errno(GetLastError()); |
| return -1; |
| } |
| } |
| if (QueryInformationProcessProc(prs, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) < 0) { |
| set_win32_errno(GetLastError()); |
| return -1; |
| } |
| |
| if (pbi.PebBaseAddress != NULL) { |
| if (ReadProcessMemory(prs, (LPCVOID)pbi.PebBaseAddress, &peb, sizeof(peb), NULL) == 0) { |
| set_win32_errno(GetLastError()); |
| return -1; |
| } |
| |
| if (peb.ProcessParameters != NULL) { |
| if (ReadProcessMemory(prs, (LPCVOID)peb.ProcessParameters, &upa, sizeof(upa), NULL) == 0) { |
| set_win32_errno(GetLastError()); |
| return -1; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int write_unicode_string(OutputStream * out, HANDLE prs, UNICODE_STRING str, const char * name) { |
| if (str.Buffer != NULL) { |
| wchar_t w_fnm[FILE_PATH_SIZE]; |
| SIZE_T buff_size = str.Length; |
| SIZE_T read_size = 0; |
| memset(w_fnm, 0, sizeof(w_fnm)); |
| if (buff_size > sizeof(w_fnm)) buff_size = sizeof(w_fnm); |
| if (ReadProcessMemory(prs, (LPCVOID)str.Buffer, w_fnm, buff_size, &read_size)) { |
| char a_fnm[FILE_PATH_SIZE * 4]; |
| size_t k = wcslen(w_fnm); |
| int n = WideCharToMultiByte(CP_UTF8, 0, w_fnm, (int)k, a_fnm, sizeof(a_fnm), NULL, NULL); |
| a_fnm[n] = 0; |
| write_stream(out, ','); |
| json_write_string(out, name); |
| write_stream(out, ':'); |
| json_write_string(out, a_fnm); |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static void write_time(OutputStream * out, FILETIME tm, int64_t base, const char * name) { |
| int64_t n = (((int64_t)tm.dwLowDateTime | ((int64_t)tm.dwHighDateTime << 32)) - base) / 10000; |
| |
| write_stream(out, ','); |
| |
| json_write_string(out, name); |
| write_stream(out, ':'); |
| if (n < 0) n = 0; |
| json_write_int64(out, n); |
| } |
| |
| static void write_process_context(OutputStream * out, char * id, pid_t pid, HANDLE prs) { |
| write_stream(out, '{'); |
| |
| json_write_string(out, "ID"); |
| write_stream(out, ':'); |
| json_write_string(out, id); |
| |
| write_stream(out, ','); |
| |
| json_write_string(out, "PID"); |
| write_stream(out, ':'); |
| json_write_ulong(out, pid); |
| |
| write_unicode_string(out, prs, upa.ImagePathName, "File"); |
| write_unicode_string(out, prs, upa.CurrentDirectoryPath, "CWD"); |
| |
| { |
| FILETIME c_time, e_time, k_time, u_time; |
| if (GetProcessTimes(prs, &c_time, &e_time, &k_time, &u_time)) { |
| static int64_t system_start_time = 0; /* In FILETIME format: 100-nanosecond intervals since January 1, 1601 (UTC). */ |
| if (system_start_time == 0) { |
| HKEY key; |
| if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, |
| L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", |
| 0, KEY_READ, &key) == ERROR_SUCCESS) { |
| wchar_t buf[FILE_PATH_SIZE]; |
| DWORD size = sizeof(buf); |
| memset(buf, 0, sizeof(buf)); |
| if (RegQueryValueExW(key, |
| L"PagingFiles", |
| NULL, NULL, (LPBYTE)buf, &size) == ERROR_SUCCESS) { |
| WIN32_FIND_DATAW data; |
| HANDLE h = INVALID_HANDLE_VALUE; |
| int n = 0; |
| while (n < FILE_PATH_SIZE && buf[n] != 0) n++; |
| while (n > 0 && buf[n - 1] != ' ') n--; |
| while (n > 0 && buf[n - 1] == ' ') n--; |
| while (n > 0 && buf[n - 1] != ' ') n--; |
| while (n > 0 && buf[n - 1] == ' ') n--; |
| buf[n] = 0; |
| h = FindFirstFileW(buf, &data); |
| if (h != INVALID_HANDLE_VALUE) { |
| system_start_time = (int64_t)data.ftLastWriteTime.dwLowDateTime | ((int64_t)data.ftLastWriteTime.dwHighDateTime << 32); |
| FindClose(h); |
| } |
| } |
| RegCloseKey(key); |
| } |
| } |
| if (system_start_time == 0) { |
| SYSTEMTIME st; |
| FILETIME ft; |
| GetSystemTime(&st); |
| if (SystemTimeToFileTime(&st, &ft)) { |
| system_start_time = (int64_t)ft.dwLowDateTime | ((int64_t)ft.dwHighDateTime << 32); |
| system_start_time -= (int64_t)GetTickCount() * 10000; /* Note: GetTickCount() is valid only first 49 days */ |
| } |
| } |
| if (system_start_time != 0) { |
| write_time(out, c_time, system_start_time, "StartTime"); |
| } |
| write_time(out, k_time, 0, "STime"); |
| write_time(out, u_time, 0, "UTime"); |
| } |
| } |
| |
| write_stream(out, '}'); |
| } |
| |
| static void command_get_context(char * token, Channel * c) { |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| int err = 0; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| pid = id2pid(id, &parent); |
| if (parent != 0) { |
| write_errno(&c->out, err); |
| write_stringz(&c->out, "null"); |
| } |
| else if (pid != 0) { |
| HANDLE prs = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); |
| if (prs == NULL) err = set_win32_errno(GetLastError()); |
| if (err == 0 && get_process_info(prs) < 0) err = errno; |
| write_errno(&c->out, err); |
| if (err == 0) { |
| write_process_context(&c->out, id, pid, prs); |
| write_stream(&c->out, 0); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| if (prs != NULL) CloseHandle(prs); |
| } |
| else { |
| write_errno(&c->out, err); |
| write_stringz(&c->out, "null"); |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_children(char * token, Channel * c) { |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| int err = 0; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| pid = id2pid(id, &parent); |
| |
| if (parent != 0) { |
| /* Children of a thread: none */ |
| write_errno(&c->out, 0); |
| write_stringz(&c->out, "null"); |
| } |
| else if (pid != 0) { |
| /* Children of a process: threads */ |
| /* TODO: enumerate threads */ |
| write_errno(&c->out, 0); |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| /* Children of the root: processes */ |
| typedef BOOL (WINAPI * EnumProcessesProcType)(DWORD *, DWORD, DWORD *); |
| static EnumProcessesProcType EnumProcessesProc; |
| HANDLE heap = GetProcessHeap(); |
| DWORD * prs_ids = NULL; |
| int prs_cnt = 0; |
| if (EnumProcessesProc == NULL) { |
| HINSTANCE psapi = LoadLibrary("PSAPI.DLL"); |
| if (psapi == NULL) { |
| err = set_win32_errno(GetLastError()); |
| } |
| else { |
| EnumProcessesProc = (EnumProcessesProcType)GetProcAddress(psapi, "EnumProcesses"); |
| if (EnumProcessesProc == NULL) err = set_win32_errno(GetLastError()); |
| } |
| } |
| if (err == 0) { |
| DWORD size_allocated = 128; |
| DWORD size_returned = 0; |
| do { |
| size_allocated *= 2; |
| if (prs_ids != NULL) HeapFree(heap, 0, prs_ids); |
| prs_ids = (DWORD *)HeapAlloc(heap, 0, size_allocated); |
| if (prs_ids == NULL) { |
| err = set_win32_errno(GetLastError()); |
| break; |
| } |
| if (!EnumProcessesProc(prs_ids, size_allocated, &size_returned)) { |
| err = set_win32_errno(GetLastError()); |
| break; |
| } |
| } |
| while (size_returned == size_allocated); |
| prs_cnt = size_returned / sizeof(DWORD); |
| } |
| write_errno(&c->out, err); |
| if (err == 0) { |
| int pos = 0; |
| int cnt = 0; |
| write_stream(&c->out, '['); |
| for (pos = 0; pos < prs_cnt; pos++) { |
| if (prs_ids[pos] == 0) continue; |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, pid2id(prs_ids[pos], 0)); |
| cnt++; |
| } |
| write_stream(&c->out, ']'); |
| write_stream(&c->out, 0); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| if (prs_ids != NULL) HeapFree(heap, 0, prs_ids); |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_command_line(char * token, Channel * c) { |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| int err = 0; |
| HANDLE prs = NULL; |
| wchar_t * cmd = NULL; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| pid = id2pid(id, &parent); |
| if (pid != 0 && parent == 0) { |
| prs = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); |
| if (prs == NULL) err = set_win32_errno(GetLastError()); |
| } |
| else { |
| err = ERR_INV_CONTEXT; |
| } |
| if (err == 0 && get_process_info(prs) < 0) err = errno; |
| if (err == 0 && upa.CommandLine.Buffer != NULL) { |
| SIZE_T cmd_size = upa.CommandLine.Length; |
| SIZE_T read_size = 0; |
| cmd = (wchar_t *)loc_alloc(cmd_size); |
| if (ReadProcessMemory(prs, (LPCVOID)upa.CommandLine.Buffer, cmd, cmd_size, &read_size) == 0) { |
| err = set_win32_errno(GetLastError()); |
| } |
| } |
| if (prs != NULL) CloseHandle(prs); |
| |
| write_errno(&c->out, err); |
| |
| if (err == 0 && cmd != NULL) { |
| wchar_t * p = cmd; |
| wchar_t * e = cmd + upa.CommandLine.Length / sizeof(wchar_t); |
| int cnt = 0; |
| write_stream(&c->out, '['); |
| while (p < e && *p) { |
| int quotation = 0; |
| if (*p == ' ') { p++; continue; } |
| if (*p == '\t') { p++; continue; } |
| if (cnt > 0) write_stream(&c->out, ','); |
| write_stream(&c->out, '"'); |
| while (p < e && *p) { |
| char buf[0x100]; |
| unsigned k = 0; |
| while (p < e && *p && k < sizeof(buf) / 4) { |
| if (*p == '"' || *p == '\\' || *p == ' ' || *p == '\t') break; |
| p++; |
| k++; |
| } |
| if (k > 0) { |
| int i = 0; |
| int n = WideCharToMultiByte(CP_UTF8, 0, p - k, k, buf, sizeof(buf), NULL, NULL); |
| while (i < n) json_write_char(&c->out, buf[i++]); |
| if (p == e || *p == 0) break; |
| } |
| if (*p == '"') { |
| quotation = !quotation; |
| p++; |
| } |
| else if (*p == '\\') { |
| p++; |
| if (p == e) { |
| json_write_char(&c->out, '\\'); |
| } |
| else if (*p == '"') { |
| json_write_char(&c->out, '"'); |
| p++; |
| } |
| else if (*p == '\\') { |
| json_write_char(&c->out, '\\'); |
| p++; |
| } |
| else { |
| json_write_char(&c->out, '\\'); |
| } |
| } |
| else if (*p == ' ' || *p == '\t') { |
| p++; |
| if (!quotation) break; |
| json_write_char(&c->out, ' '); |
| } |
| else { |
| assert(k > 0); |
| } |
| } |
| write_stream(&c->out, '"'); |
| cnt++; |
| } |
| write_stream(&c->out, ']'); |
| write_stream(&c->out, 0); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| loc_free(cmd); |
| } |
| |
| static void command_get_environment(char * token, Channel * c) { |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| int err = 0; |
| HANDLE prs = NULL; |
| wchar_t * env = NULL; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| pid = id2pid(id, &parent); |
| if (pid != 0 && parent == 0) { |
| prs = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); |
| if (prs == NULL) err = set_win32_errno(GetLastError()); |
| } |
| else { |
| err = ERR_INV_CONTEXT; |
| } |
| if (err == 0 && get_process_info(prs) < 0) err = errno; |
| if (err == 0 && upa.Environment != NULL) { |
| wchar_t buf[0x100]; |
| SIZE_T buf_pos = 0; |
| SIZE_T buf_len = 0; |
| SIZE_T env_size = 0; |
| int cnt = 0; |
| |
| for (;;) { |
| if (buf_pos >= buf_len) { |
| SIZE_T len = 0; |
| if (ReadProcessMemory(prs, (LPCVOID)((SIZE_T)upa.Environment + env_size), buf, sizeof(buf), &len) == 0) { |
| err = set_win32_errno(GetLastError()); |
| break; |
| } |
| buf_pos = 0; |
| buf_len = len / sizeof(wchar_t); |
| } |
| env_size += sizeof(wchar_t); |
| if (buf[buf_pos++] == 0) { |
| cnt++; |
| if (cnt == 2) break; |
| } |
| else { |
| cnt = 0; |
| } |
| } |
| |
| if (err == 0) { |
| env = (wchar_t *)loc_alloc(env_size); |
| if (ReadProcessMemory(prs, (LPCVOID)upa.Environment, env, env_size, &buf_len) == 0) { |
| err = set_win32_errno(GetLastError()); |
| } |
| } |
| } |
| if (prs != NULL) CloseHandle(prs); |
| |
| write_errno(&c->out, err); |
| |
| if (err == 0 && env != NULL) { |
| wchar_t * p = env; |
| int cnt = 0; |
| write_stream(&c->out, '['); |
| while (*p) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| write_stream(&c->out, '"'); |
| while (*p) { |
| char buf[0x100]; |
| unsigned k = 0; |
| int n = 0, i = 0; |
| while (*p && k < sizeof(buf) / 4) { p++; k++; } |
| n = WideCharToMultiByte(CP_UTF8, 0, p - k, k, buf, sizeof(buf), NULL, NULL); |
| while (i < n) json_write_char(&c->out, buf[i++]); |
| } |
| p++; |
| write_stream(&c->out, '"'); |
| cnt++; |
| } |
| write_stream(&c->out, ']'); |
| write_stream(&c->out, 0); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| loc_free(env); |
| } |
| |
| #else |
| |
| #include <unistd.h> |
| #include <pwd.h> |
| #include <grp.h> |
| #if defined(__sun__) |
| #include <procfs.h> |
| #else |
| #include <linux/param.h> |
| #endif |
| |
| #define BUF_EOF (-1) |
| |
| static char buf[1024]; |
| static int buf_fd = -1; |
| static int buf_pos = 0; |
| static int buf_len = 0; |
| static int buf_ch = 0; |
| |
| static void next_ch(void) { |
| while (buf_len >= 0 && buf_pos >= buf_len) { |
| buf_pos = 0; |
| buf_len = read(buf_fd, buf, sizeof(buf)); |
| if (buf_len == 0) buf_len = -1; |
| } |
| if (buf_len < 0) { |
| buf_ch = BUF_EOF; |
| } |
| else { |
| buf_ch = (unsigned char)buf[buf_pos++]; |
| } |
| } |
| |
| static void first_ch(int fd) { |
| buf_fd = fd; |
| buf_pos = 0; |
| buf_len = 0; |
| next_ch(); |
| } |
| |
| static void write_string_array(OutputStream * out, int f) { |
| int cnt = 0; |
| first_ch(f); |
| write_stream(out, '['); |
| while (buf_ch != BUF_EOF && buf_ch != 0) { |
| if (cnt > 0) write_stream(out, ','); |
| write_stream(out, '"'); |
| do { |
| json_write_char(out, buf_ch); |
| next_ch(); |
| } |
| while (buf_ch != BUF_EOF && buf_ch != 0); |
| next_ch(); |
| write_stream(out, '"'); |
| cnt++; |
| } |
| write_stream(out, ']'); |
| } |
| |
| #if defined(__sun__) |
| #define PROC_CWD_PATH "path/cwd" |
| #define PROC_ROOT_PATH "path/root" |
| #define PROC_EXE_PATH "path/a.out" |
| #define PROC_PSINFO_STAT "psinfo" |
| #define JIFFIES_TO_MSEC_FACTOR (1) |
| #else |
| #define PROC_CWD_PATH "cwd" |
| #define PROC_ROOT_PATH "root" |
| #define PROC_EXE_PATH "exe" |
| #define PROC_PSINFO_STAT "stat" |
| #define JIFFIES_TO_MSEC_FACTOR (1000/HZ) |
| #endif |
| |
| static void write_context(OutputStream * out, char * id, char * parent_id, char * dir) { |
| char fnm[FILE_PATH_SIZE + 1]; |
| int sz; |
| int f; |
| |
| write_stream(out, '{'); |
| |
| if (chdir(dir) >= 0) { |
| if ((sz = readlink(PROC_CWD_PATH, fnm, FILE_PATH_SIZE)) > 0) { |
| fnm[sz] = 0; |
| json_write_string(out, "CWD"); |
| write_stream(out, ':'); |
| json_write_string(out, fnm); |
| write_stream(out, ','); |
| } |
| |
| if ((sz = readlink(PROC_ROOT_PATH, fnm, FILE_PATH_SIZE)) > 0) { |
| fnm[sz] = 0; |
| json_write_string(out, "Root"); |
| write_stream(out, ':'); |
| json_write_string(out, fnm); |
| write_stream(out, ','); |
| } |
| |
| if ((sz = readlink(PROC_EXE_PATH, fnm, FILE_PATH_SIZE)) > 0) { |
| fnm[sz] = 0; |
| json_write_string(out, "Exe"); |
| write_stream(out, ':'); |
| json_write_string(out, fnm); |
| write_stream(out, ','); |
| json_write_string(out, "ExeType"); |
| write_stream(out, ':'); |
| json_write_long(out, EXETYPE_USER); |
| write_stream(out, ','); |
| } |
| else if (errno == ENOENT) { |
| json_write_string(out, "ExeType"); |
| write_stream(out, ':'); |
| json_write_long(out, EXETYPE_KERNEL); |
| write_stream(out, ','); |
| } |
| else if (errno == EACCES) { |
| json_write_string(out, "ExeType"); |
| write_stream(out, ':'); |
| json_write_long(out, EXETYPE_ACCESS_DENIED); |
| write_stream(out, ','); |
| } |
| |
| f = open(PROC_PSINFO_STAT, O_RDONLY); |
| #if defined(__sun__) |
| } else { |
| /* On Solaris, "chdir /proc/<pid>" is only allowed for the owner, */ |
| /* but psinfo is readable for all. So, try this as fallback. */ |
| snprintf(fnm, FILE_PATH_SIZE, "/%s/%s", dir, PROC_PSINFO_STAT); |
| fnm[FILE_PATH_SIZE]=0; |
| f = open(fnm, O_RDONLY); |
| } |
| { |
| #endif |
| if (f >= 0) { |
| struct stat st; |
| if (fstat(f, &st) == 0) { |
| struct passwd * pwd; |
| struct group * grp; |
| |
| json_write_string(out, "UID"); |
| write_stream(out, ':'); |
| json_write_long(out, st.st_uid); |
| write_stream(out, ','); |
| |
| json_write_string(out, "UGID"); |
| write_stream(out, ':'); |
| json_write_long(out, st.st_gid); |
| write_stream(out, ','); |
| |
| pwd = getpwuid(st.st_uid); |
| if (pwd != NULL) { |
| json_write_string(out, "UserName"); |
| write_stream(out, ':'); |
| json_write_string(out, pwd->pw_name); |
| write_stream(out, ','); |
| } |
| |
| grp = getgrgid(st.st_gid); |
| if (grp != NULL) { |
| json_write_string(out, "GroupName"); |
| write_stream(out, ':'); |
| json_write_string(out, grp->gr_name); |
| write_stream(out, ','); |
| } |
| } |
| |
| memset(buf, 0, sizeof(buf)); |
| if ((sz = read(f, buf, sizeof(buf) - 1)) > 0) { |
| char * str = buf; |
| int pid = 0; /* The process ID. */ |
| char * comm = fnm; /* The filename of the executable, in parentheses. This is visible */ |
| /* whether or not the executable is swapped out. */ |
| char state = 0; /* One character from the string "RSDZTW" where R is running, S is */ |
| /* sleeping in an interruptible wait, D is waiting in uninterruptible */ |
| /* disk sleep, Z is zombie, T is traced or stopped (on a signal), and W */ |
| /* is paging. */ |
| int ppid = 0; /* The PID of the parent. */ |
| int pgrp = 0; /* The process group ID of the process. */ |
| int session = 0; /* The session ID of the process. */ |
| int tty_nr = 0; /* The tty the process uses. */ |
| int tpgid = 0; /* The process group ID of the process which currently owns the tty that */ |
| /* the process is connected to. */ |
| unsigned long flags = 0; /* The kernel flags word of the process. For bit meanings, see the PF_* */ |
| /* defines in <linux/sched.h>. Details depend on the kernel version. */ |
| unsigned long minflt = 0; /* The number of minor faults the process has made which have not */ |
| /* required loading a memory page from disk. */ |
| unsigned long cminflt = 0; /* The number of minor faults that the process's waited-for children */ |
| /* have made. */ |
| unsigned long majflt = 0; /* The number of major faults the process has made which have required */ |
| /* loading a memory page from disk. */ |
| unsigned long cmajflt = 0; /* The number of major faults that the process's waited-for children */ |
| /* have made. */ |
| unsigned long utime = 0; /* The number of jiffies that this process has been scheduled in user */ |
| /* mode. */ |
| unsigned long stime = 0; /* The number of jiffies that this process has been scheduled in kernel */ |
| /* mode. */ |
| long cutime = 0; /* The number of jiffies that this process's waited-for children have */ |
| /* been scheduled in user mode. (See also times(2).) */ |
| long cstime = 0; /* The number of jiffies that this process's waited-for children have */ |
| /* been scheduled in kernel mode. */ |
| long priority = 0; /* The standard nice value, plus fifteen. The value is never negative */ |
| /* in the kernel. */ |
| long nice = 0; /* The nice value ranges from 19 (nicest) to -19 (not nice to others). */ |
| long dummy = 0; /* This value is hard coded to 0 as a placeholder for a removed field. */ |
| long itrealvalue = 0; /* The time in jiffies before the next SIGALRM is sent to the process */ |
| /* due to an interval timer. */ |
| unsigned long starttime = 0;/* The time in jiffies the process started after system boot. */ |
| unsigned long vsize = 0; /* Virtual memory size in bytes. */ |
| long rss = 0; /* Resident Set Size: number of pages the process has in real memory, */ |
| /* minus 3 for administrative purposes. This is just the pages which */ |
| /* count towards text, data, or stack space. This does not include */ |
| /* pages which have not been demand-loaded in, or which are swapped out. */ |
| unsigned long rlim = 0; /* Current limit in bytes on the rss of the process (usually 4294967295 */ |
| /* on i386). */ |
| unsigned long startcode = 0;/* The address above which program text can run. */ |
| unsigned long endcode = 0; /* The address below which program text can run. */ |
| unsigned long startstack =0;/* The address of the start of the stack. */ |
| unsigned long kstkesp = 0; /* The current value of esp (stack pointer), as found in the kernel */ |
| /* stack page for the process. */ |
| unsigned long kstkeip = 0; /* The current EIP (instruction pointer). */ |
| unsigned long signal = 0; /* The bitmap of pending signals. */ |
| unsigned long blocked = 0; /* The bitmap of blocked signals. */ |
| unsigned long sigignore = 0;/* The bitmap of ignored signals. */ |
| unsigned long sigcatch = 0; /* The bitmap of caught signals. */ |
| unsigned long wchan = 0; /* This is the "channel" in which the process is waiting. It is the */ |
| /* address of a system call, and can be looked up in a namelist if you */ |
| /* need a textual name. (If you have an up-to-date /etc/psdatabase, */ |
| /* then try ps -l to see the WCHAN field in action.) */ |
| unsigned long nswap = 0; /* Number of pages swapped (not maintained). */ |
| unsigned long cnswap = 0; /* Cumulative nswap for child processes (not maintained). */ |
| int exit_signal = 0; /* Signal to be sent to parent when we die. */ |
| int processor = 0; /* CPU number last executed on. */ |
| unsigned long rt_priority=0;/* Real-time scheduling priority (see sched_setscheduler(2)). */ |
| unsigned long policy = 0; /* Scheduling policy (see sched_setscheduler(2)). */ |
| |
| #if defined(__sun__) |
| psinfo_t *psinfo = (psinfo_t *) &buf; |
| pid = psinfo->pr_pid; |
| strncpy(comm, psinfo->pr_fname, PRFNSZ); |
| comm[PRFNSZ] = 0; |
| state = psinfo->pr_lwp.pr_sname; |
| ppid = psinfo->pr_ppid; |
| pgrp = psinfo->pr_pgid; |
| session = psinfo->pr_sid; |
| /* |
| * Solaris: A number of fields either have no equivalent on Solaris, |
| * or they are only available from additional files in /proc. See |
| * http://docs.oracle.com/cd/E19253-01/816-5174/6mbb98ui2/index.html |
| * - pstatus_t would provide System CPU Time |
| * - prusage_t would provide page faults |
| * |
| * For an initial port, we don't care about this extra information, |
| * and focus on the psinfo_t structure only to save some performance. |
| * Respective flags are still kept here as reference -- they will |
| * be sent to the client with initialized default values. |
| */ |
| //tty_nr = psinfo->pr_ttydev; |
| //tpgid = |
| //flags = |
| //minflt = |
| //cminflt = |
| //majflt = |
| //cmajflt = |
| utime = psinfo->pr_time.tv_sec * 1000 + psinfo->pr_time.tv_nsec / 1000; |
| //stime = |
| cutime = psinfo->pr_ctime.tv_sec * 1000 + psinfo->pr_ctime.tv_nsec / 1000; |
| //cstime = |
| priority = psinfo->pr_lwp.pr_pri; |
| nice = psinfo->pr_lwp.pr_nice; |
| //dummy = |
| //itrealvalue = |
| starttime = psinfo->pr_start.tv_sec * 1000 + psinfo->pr_start.tv_nsec / 1000; |
| vsize = psinfo->pr_size * 1024; |
| rss = psinfo->pr_rssize * 1024; |
| //rlim = |
| //startcode = |
| //endcode = |
| //startstack = |
| //kstkesp = |
| //kstkeip = |
| //signal = |
| //blocked = |
| //sigignore = |
| //sigcatch = |
| //wchan = |
| //nswap = |
| //cnswap = |
| //exit_signal = |
| processor = psinfo->pr_lwp.pr_onpro; |
| //rt_priority = |
| //policy = |
| #else |
| pid = (int)strtol(str, &str, 10); |
| while (*str == ' ') str++; |
| |
| if (*str == '(') str++; |
| sz = strlen(str); |
| while (sz > 0 && str[sz] != ')') sz--; |
| memcpy(comm, str, sz); |
| comm[sz] = 0; |
| str += sz; |
| if (*str == ')') str++; |
| while (*str == ' ') str++; |
| |
| sscanf(str, |
| "%c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu", |
| &state, &ppid, &pgrp, &session, &tty_nr, &tpgid, &flags, |
| &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime, &cutime, &cstime, |
| &priority, &nice, &dummy, &itrealvalue, &starttime, &vsize, &rss, &rlim, |
| &startcode, &endcode, &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore, &sigcatch, |
| &wchan, &nswap, &cnswap, &exit_signal, &processor, &rt_priority, &policy); |
| #endif |
| json_write_string(out, "PID"); |
| write_stream(out, ':'); |
| json_write_long(out, pid); |
| write_stream(out, ','); |
| |
| json_write_string(out, "File"); |
| write_stream(out, ':'); |
| json_write_string(out, comm); |
| write_stream(out, ','); |
| |
| json_write_string(out, "State"); |
| write_stream(out, ':'); |
| write_stream(out, '"'); |
| json_write_char(out, state); |
| write_stream(out, '"'); |
| write_stream(out, ','); |
| |
| if (ppid > 0) { |
| json_write_string(out, "PPID"); |
| write_stream(out, ':'); |
| json_write_long(out, ppid); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "PGRP"); |
| write_stream(out, ':'); |
| json_write_long(out, pgrp); |
| write_stream(out, ','); |
| |
| json_write_string(out, "Session"); |
| write_stream(out, ':'); |
| json_write_long(out, session); |
| write_stream(out, ','); |
| |
| if (tty_nr > 0) { |
| json_write_string(out, "TTY"); |
| write_stream(out, ':'); |
| json_write_long(out, tty_nr); |
| write_stream(out, ','); |
| } |
| |
| if (tpgid > 0) { |
| json_write_string(out, "TGID"); |
| write_stream(out, ':'); |
| json_write_long(out, tpgid); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "Flags"); |
| write_stream(out, ':'); |
| json_write_ulong(out, flags); |
| write_stream(out, ','); |
| |
| json_write_string(out, "MinFlt"); |
| write_stream(out, ':'); |
| json_write_ulong(out, minflt); |
| write_stream(out, ','); |
| |
| json_write_string(out, "CMinFlt"); |
| write_stream(out, ':'); |
| json_write_ulong(out, cminflt); |
| write_stream(out, ','); |
| |
| json_write_string(out, "MajFlt"); |
| write_stream(out, ':'); |
| json_write_ulong(out, majflt); |
| write_stream(out, ','); |
| |
| json_write_string(out, "CMajFlt"); |
| write_stream(out, ':'); |
| json_write_ulong(out, cmajflt); |
| write_stream(out, ','); |
| |
| json_write_string(out, "UTime"); |
| write_stream(out, ':'); |
| json_write_uint64(out, (uint64_t)utime * JIFFIES_TO_MSEC_FACTOR); |
| write_stream(out, ','); |
| |
| json_write_string(out, "STime"); |
| write_stream(out, ':'); |
| json_write_uint64(out, (uint64_t)stime * JIFFIES_TO_MSEC_FACTOR); |
| write_stream(out, ','); |
| |
| json_write_string(out, "CUTime"); |
| write_stream(out, ':'); |
| json_write_uint64(out, (uint64_t)cutime * JIFFIES_TO_MSEC_FACTOR); |
| write_stream(out, ','); |
| |
| json_write_string(out, "CSTime"); |
| write_stream(out, ':'); |
| json_write_uint64(out, (uint64_t)cstime * JIFFIES_TO_MSEC_FACTOR); |
| write_stream(out, ','); |
| |
| json_write_string(out, "Priority"); |
| write_stream(out, ':'); |
| json_write_long(out, (long)priority - 15); |
| write_stream(out, ','); |
| |
| if (nice != 0) { |
| json_write_string(out, "Nice"); |
| write_stream(out, ':'); |
| json_write_long(out, nice); |
| write_stream(out, ','); |
| } |
| |
| if (itrealvalue != 0) { |
| json_write_string(out, "ITRealValue"); |
| write_stream(out, ':'); |
| json_write_int64(out, (int64_t)itrealvalue * JIFFIES_TO_MSEC_FACTOR); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "StartTime"); |
| write_stream(out, ':'); |
| json_write_uint64(out, (uint64_t)starttime * JIFFIES_TO_MSEC_FACTOR); |
| write_stream(out, ','); |
| |
| json_write_string(out, "VSize"); |
| write_stream(out, ':'); |
| json_write_ulong(out, vsize); |
| write_stream(out, ','); |
| |
| json_write_string(out, "PSize"); |
| write_stream(out, ':'); |
| json_write_ulong(out, getpagesize()); |
| write_stream(out, ','); |
| |
| json_write_string(out, "RSS"); |
| write_stream(out, ':'); |
| json_write_long(out, rss); |
| write_stream(out, ','); |
| |
| json_write_string(out, "RLimit"); |
| write_stream(out, ':'); |
| json_write_ulong(out, rlim); |
| write_stream(out, ','); |
| |
| if (startcode != 0) { |
| json_write_string(out, "CodeStart"); |
| write_stream(out, ':'); |
| json_write_ulong(out, startcode); |
| write_stream(out, ','); |
| } |
| |
| if (endcode != 0) { |
| json_write_string(out, "CodeEnd"); |
| write_stream(out, ':'); |
| json_write_ulong(out, endcode); |
| write_stream(out, ','); |
| } |
| |
| if (startstack != 0) { |
| json_write_string(out, "StackStart"); |
| write_stream(out, ':'); |
| json_write_ulong(out, startstack); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "Signals"); |
| write_stream(out, ':'); |
| json_write_ulong(out, signal); |
| write_stream(out, ','); |
| |
| json_write_string(out, "SigBlock"); |
| write_stream(out, ':'); |
| json_write_ulong(out, blocked); |
| write_stream(out, ','); |
| |
| json_write_string(out, "SigIgnore"); |
| write_stream(out, ':'); |
| json_write_ulong(out, sigignore); |
| write_stream(out, ','); |
| |
| json_write_string(out, "SigCatch"); |
| write_stream(out, ':'); |
| json_write_ulong(out, sigcatch); |
| write_stream(out, ','); |
| |
| if (wchan != 0) { |
| json_write_string(out, "WChan"); |
| write_stream(out, ':'); |
| json_write_ulong(out, wchan); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "NSwap"); |
| write_stream(out, ':'); |
| json_write_ulong(out, nswap); |
| write_stream(out, ','); |
| |
| json_write_string(out, "CNSwap"); |
| write_stream(out, ':'); |
| json_write_ulong(out, cnswap); |
| write_stream(out, ','); |
| |
| json_write_string(out, "ExitSignal"); |
| write_stream(out, ':'); |
| json_write_long(out, exit_signal); |
| write_stream(out, ','); |
| |
| json_write_string(out, "Processor"); |
| write_stream(out, ':'); |
| json_write_long(out, processor); |
| write_stream(out, ','); |
| |
| json_write_string(out, "RTPriority"); |
| write_stream(out, ':'); |
| json_write_ulong(out, rt_priority); |
| write_stream(out, ','); |
| |
| json_write_string(out, "Policy"); |
| write_stream(out, ':'); |
| json_write_ulong(out, policy); |
| write_stream(out, ','); |
| } |
| close(f); |
| } |
| } |
| |
| if (parent_id != NULL && parent_id[0] != 0) { |
| json_write_string(out, "ParentID"); |
| write_stream(out, ':'); |
| json_write_string(out, parent_id); |
| write_stream(out, ','); |
| } |
| |
| json_write_string(out, "ID"); |
| write_stream(out, ':'); |
| json_write_string(out, id); |
| |
| write_stream(out, '}'); |
| } |
| |
| static void command_get_context(char * token, Channel * c) { |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| int err = 0; |
| char dir[FILE_PATH_SIZE]; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| pid = id2pid(id, &parent); |
| if (pid != 0) { |
| struct stat st; |
| if (parent != 0) { |
| /* Solaris: Not implemented, we don't care about LWP process children */ |
| snprintf(dir, sizeof(dir), "/proc/%d/task/%d", parent, pid); |
| } |
| else { |
| snprintf(dir, sizeof(dir), "/proc/%d", pid); |
| } |
| if (lstat(dir, &st) < 0) err = errno == ENOENT ? ESRCH : errno; |
| else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT; |
| } |
| |
| write_errno(&c->out, err); |
| |
| if (err == 0 && pid != 0) { |
| char bf[256]; |
| write_context(&c->out, id, parent == 0 ? NULL : strcpy(bf, pid2id(parent, 0)), dir); |
| write_stream(&c->out, 0); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_children(char * token, Channel * c) { |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| pid = id2pid(id, &parent); |
| |
| #if defined(__sun__) |
| /* Solaris: Process Children (LWP) support is not implemented at this time. */ |
| /* Therefore, we always return null for children, without actually checking. */ |
| if (pid != 0) parent = pid; |
| #endif |
| |
| if (parent != 0) { |
| write_errno(&c->out, 0); |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| DIR * proc = NULL; |
| char dir[FILE_PATH_SIZE]; |
| if (pid == 0) strcpy(dir, "/proc"); |
| else { |
| /* Solaris: LWP is not implemented */ |
| snprintf(dir, sizeof(dir), "/proc/%d/task", pid); |
| } |
| proc = opendir(dir); |
| if (proc == NULL) { |
| int err = errno; |
| if (pid != 0 && err == ENOENT) { |
| struct stat buf; |
| snprintf(dir, sizeof(dir), "/proc/%d", pid); |
| if (stat(dir, &buf) == 0) { |
| /* Zombie */ |
| err = 0; |
| } |
| else { |
| err = ESRCH; |
| } |
| } |
| write_errno(&c->out, err); |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| int cnt = 0; |
| write_errno(&c->out, 0); |
| write_stream(&c->out, '['); |
| for (;;) { |
| struct dirent * ent = readdir(proc); |
| if (ent == NULL) break; |
| if (ent->d_name[0] >= '1' && ent->d_name[0] <= '9') { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, pid2id(atol(ent->d_name), pid)); |
| cnt++; |
| } |
| } |
| write_stream(&c->out, ']'); |
| write_stream(&c->out, 0); |
| closedir(proc); |
| } |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_command_line(char * token, Channel * c) { |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| int err = 0; |
| char dir[FILE_PATH_SIZE]; |
| int f = -1; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| pid = id2pid(id, &parent); |
| if (pid != 0 && parent == 0) { |
| struct stat st; |
| snprintf(dir, sizeof(dir), "/proc/%d", pid); |
| if (lstat(dir, &st) < 0) err = errno == ENOENT ? ESRCH : errno; |
| else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT; |
| } |
| else { |
| err = ERR_INV_CONTEXT; |
| } |
| |
| #if defined(__sun__) |
| /* Solaris only saves the first 80 characters of the command-line in psinfo. |
| * Getting the full untruncated command-line requires access to process memory: |
| * http://praveen.kumar.in/2010/02/24/getting-untruncated-command-line-options-passed-to-a-solaris-process/ |
| * |
| * We don't care about these details and just send the first 80 characters, like ps does. |
| */ |
| if (err == 0) { |
| snprintf(dir, sizeof(dir), "/proc/%d/psinfo", pid); |
| if ((f = open(dir, O_RDONLY)) < 0) err = errno == ENOENT ? ESRCH : errno; |
| } |
| write_errno(&c->out, err); |
| if (err == 0) { |
| psinfo_t psinfo; |
| ssize_t bytes_read; |
| bytes_read = read(f, &psinfo, sizeof(psinfo_t)); |
| if (bytes_read == sizeof(psinfo_t)) { |
| write_stream(&c->out, '['); |
| /* We don't care about tokenizing the command. */ |
| /* Solaris seems to ensure that there is no " character in the command. */ |
| write_stream(&c->out, '"'); |
| write_stringz(&c->out, psinfo.pr_psargs); |
| write_stream(&c->out, '"'); |
| write_stream(&c->out, ']'); |
| write_stream(&c->out, 0); |
| } else { |
| write_stringz(&c->out, "null"); |
| } |
| close(f); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| #else |
| if (err == 0 && chdir(dir) < 0) err = errno; |
| if (err == 0 && (f = open("cmdline", O_RDONLY)) < 0) err = errno; |
| write_errno(&c->out, err); |
| if (err == 0) { |
| write_string_array(&c->out, f); |
| close(f); |
| write_stream(&c->out, 0); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| #endif |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_environment(char * token, Channel * c) { |
| char id[256]; |
| pid_t pid = 0; |
| pid_t parent = 0; |
| int err = 0; |
| char dir[FILE_PATH_SIZE]; |
| int f = -1; |
| |
| json_read_string(&c->inp, id, sizeof(id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, token); |
| |
| #if defined(__sun__) |
| /* Solaris: Accessing the environment would require accessing process memory. */ |
| /* This is not implemented at this time -> Just return null for any context. */ |
| write_errno(&c->out, ENOENT); |
| write_stringz(&c->out, "null"); |
| #else |
| pid = id2pid(id, &parent); |
| if (pid != 0 && parent == 0) { |
| struct stat st; |
| snprintf(dir, sizeof(dir), "/proc/%d", pid); |
| if (lstat(dir, &st) < 0) err = errno == ENOENT ? ESRCH : errno; |
| else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT; |
| } |
| else { |
| err = ERR_INV_CONTEXT; |
| } |
| |
| if (err == 0 && chdir(dir) < 0) err = errno; |
| if (err == 0 && (f = open("environ", O_RDONLY)) < 0) err = errno; |
| |
| write_errno(&c->out, err); |
| |
| if (err == 0) { |
| write_string_array(&c->out, f); |
| close(f); |
| write_stream(&c->out, 0); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| #endif |
| write_stream(&c->out, MARKER_EOM); |
| } |
| #endif |
| |
| extern void ini_sys_mon_service(Protocol * proto) { |
| add_command_handler(proto, SYS_MON, "getContext", command_get_context); |
| add_command_handler(proto, SYS_MON, "getChildren", command_get_children); |
| add_command_handler(proto, SYS_MON, "getCommandLine", command_get_command_line); |
| add_command_handler(proto, SYS_MON, "getEnvironment", command_get_environment); |
| } |
| |
| #endif /* SERVICE_SysMonitor */ |