1 /*
2  * libstatgrab
3  * https://libstatgrab.org
4  * Copyright (C) 2003-2004 Peter Saunders
5  * Copyright (C) 2003-2019 Tim Bishop
6  * Copyright (C) 2003-2013 Adam Sampson
7  * Copyright (C) 2012-2019 Jens Rehsack
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301, USA.
23  */
24 
25 #include "tools.h"
26 #ifdef WIN32
27 #include <pdhmsg.h>
28 
29 static HQUERY h_query;
30 
31 static HCOUNTER current_han[SG_WIN32_SIZE];
32 
33 static char **diskio_names;
34 static HCOUNTER *diskio_rhan;
35 static HCOUNTER *diskio_whan;
36 static int diskio_no;
37 
38 static int is_started = 0;
39 
40 /*static char **netio_names;
41 static HCOUNTER *netio_rhan;
42 static HCOUNTER *netio_shan;
43 static int netio_no;*/
44 
add_counter(const char * fullCounterPath,HCOUNTER * phCounter)45 int add_counter(const char *fullCounterPath, HCOUNTER *phCounter)
46 {
47 	PDH_STATUS pdh_status;
48 
49 	pdh_status = PdhAddCounter(h_query, fullCounterPath,
50 			0, phCounter);
51 	if(pdh_status != ERROR_SUCCESS) {
52 		//sg_set_error(SG_ERROR_PDHADD, fullCounterPath);
53 		phCounter = NULL;
54 		return -1;
55 	}
56 	return 0;
57 }
58 
read_counter_double(pdh_enum counter,double * result)59 int read_counter_double(pdh_enum counter, double *result)
60 {
61 	PDH_STATUS pdh_status;
62 	PDH_FMT_COUNTERVALUE *item_buf;
63 	HCOUNTER hcounter = current_han[counter];
64 
65 	if(hcounter == NULL)
66 		return -1;
67 
68 	item_buf = sg_malloc(sizeof(PDH_FMT_COUNTERVALUE));
69 	if (item_buf == NULL) {
70 		return -1;
71 	}
72 
73 	pdh_status = PdhGetFormattedCounterValue(hcounter, PDH_FMT_DOUBLE, NULL,
74 			item_buf);
75 	if(pdh_status != ERROR_SUCCESS) {
76 		free(item_buf);
77 		return -1;
78 	}
79 	*result = item_buf->doubleValue;
80 	free(item_buf);
81 	return 0;
82 }
83 
read_counter_large_int(HCOUNTER hcounter,long long * result)84 static int read_counter_large_int(HCOUNTER hcounter, long long *result)
85 {
86 	PDH_STATUS pdh_status;
87 	PDH_FMT_COUNTERVALUE *item_buf;
88 
89 	if(hcounter == NULL)
90 		return -1;
91 
92 	item_buf = sg_malloc(sizeof(PDH_FMT_COUNTERVALUE));
93 	if (item_buf == NULL) {
94 		return -1;
95 	}
96 
97 	pdh_status = PdhGetFormattedCounterValue(hcounter, PDH_FMT_LARGE, NULL,
98 			item_buf);
99 	if(pdh_status != ERROR_SUCCESS) {
100 		free(item_buf);
101 		/*switch(pdh_status) {
102 			case PDH_INVALID_ARGUMENT:
103 				printf("invalid argument\n");
104 				break;
105 			case PDH_INVALID_DATA:
106 				printf("invalid data\n");
107 				break;
108 			case PDH_INVALID_HANDLE:
109 				printf("invalid handle\n");
110 				break;
111 		}*/
112 		return -1;
113 	}
114 	*result = item_buf->largeValue;
115 	free(item_buf);
116 	return 0;
117 }
118 
read_counter_large(pdh_enum counter,long long * result)119 int read_counter_large(pdh_enum counter, long long *result)
120 {
121 	return read_counter_large_int(current_han[counter], result);
122 }
123 
get_instances(const char * object)124 static char *get_instances(const char *object)
125 {
126 	PDH_STATUS pdh_status;
127 	char *instancelistbuf;
128 	DWORD instancelistsize = 0;
129 	char *counterlistbuf;
130 	DWORD counterlistsize = 0;
131 
132 	/* Get necessary size of buffers */
133 	pdh_status = PdhEnumObjectItems(NULL, NULL, object, NULL,
134 			&counterlistsize, NULL, &instancelistsize,
135 			PERF_DETAIL_WIZARD, 0);
136 	/* 2k is dodgy and returns ERROR_SUCCESS even though the buffers were
137 	 * NULL */
138 	if(pdh_status == PDH_MORE_DATA || pdh_status == ERROR_SUCCESS) {
139 		instancelistbuf = sg_malloc(instancelistsize * sizeof(TCHAR));
140 		counterlistbuf = sg_malloc(counterlistsize * sizeof(TCHAR));
141 		if (instancelistbuf != NULL && counterlistbuf != NULL) {
142 			pdh_status = PdhEnumObjectItems(NULL, NULL, object,
143 					counterlistbuf, &counterlistsize,
144 					instancelistbuf, &instancelistsize,
145 					PERF_DETAIL_WIZARD, 0);
146 			if (pdh_status == ERROR_SUCCESS) {
147 				free(counterlistbuf);
148 				return instancelistbuf;
149 			}
150 		}
151 		if (counterlistbuf != NULL)
152 			free(counterlistbuf);
153 		if(instancelistbuf != NULL)
154 			free(instancelistbuf);
155 	}
156 	return NULL;
157 }
158 
159 /* Gets the instance buffer. Removes _Total item. Works out how many items
160  * there are. Returns these in a pointer list, rather than single buffer.
161  */
get_instance_list(const char * object,int * n)162 static char **get_instance_list(const char *object, int *n)
163 {
164 	char *thisinstance = NULL;
165 	char *instances = NULL;
166 	char **list;
167 	char **listtmp = NULL;
168 	int i;
169 	*n = 0;
170 
171 	instances = get_instances(object);
172 	if (instances == NULL)
173 		return NULL;
174 
175 	list = (char **)sg_malloc(sizeof(char *));
176 	if (list == NULL) {
177 		return NULL;
178 	}
179 	for (thisinstance = instances; *thisinstance != 0;
180 			thisinstance += strlen(thisinstance) + 1) {
181 		/* Skip over the _Total item */
182 		if (strcmp(thisinstance,"_Total") == 0) continue;
183 
184 		listtmp = (char **)sg_realloc(list, sizeof(char *) * ((*n)+1));
185 		if (listtmp == NULL) {
186 			goto out;
187 		}
188 		list = listtmp;
189 		list[*n] = sg_malloc(strlen(thisinstance) +1);
190 		if(list[*n] == NULL) {
191 			goto out;
192 		}
193 		list[*n] = strcpy(list[*n], thisinstance);
194 		++*n;
195 	}
196 	free (instances);
197 	return list;
198 
199 out:
200 	for (i = 0; i < *n; i++) {
201 		free(list[i]);
202 	}
203 	free(list);
204 	return NULL;
205 }
206 
get_diskio(const int no,long long * read,long long * write)207 char *get_diskio(const int no, long long *read, long long *write)
208 {
209 	int result;
210 	char *name = NULL;
211 
212 	if (no >= diskio_no || no < 0)
213 		return NULL;
214 
215 	result = read_counter_large_int(diskio_rhan[no], read);
216 	result = result + read_counter_large_int(diskio_whan[no], write);
217 	if (result) {
218 		sg_set_error_fmt(SG_ERROR_PDHREAD, "diskio");
219 		return NULL;
220 	}
221 	if (sg_update_string(&name, diskio_names[no]))
222 		return NULL;
223 	return name;
224 }
225 
226 /* We do not fail on add_counter here, as some counters may not exist on all
227  * hosts. The rest will work, and the missing/failed counters will die nicely
228  * elsewhere */
add_all_monitors()229 static int add_all_monitors()
230 {
231 	int i;
232 	char tmp[512];
233 
234 	add_counter(PDH_USER, &(current_han[SG_WIN32_PROC_USER]));
235 	add_counter(PDH_PRIV, &(current_han[SG_WIN32_PROC_PRIV]));
236 	add_counter(PDH_IDLE, &(current_han[SG_WIN32_PROC_IDLE]));
237 	add_counter(PDH_INTER, &(current_han[SG_WIN32_PROC_INT]));
238 	add_counter(PDH_MEM_CACHE, &(current_han[SG_WIN32_MEM_CACHE]));
239 	add_counter(PDH_UPTIME, &(current_han[SG_WIN32_UPTIME]));
240 	add_counter(PDH_PAGEIN, &(current_han[SG_WIN32_PAGEIN]));
241 	add_counter(PDH_PAGEOUT, &(current_han[SG_WIN32_PAGEOUT]));
242 
243 	diskio_names = get_instance_list("PhysicalDisk", &diskio_no);
244 	if (diskio_names != NULL) {
245 		diskio_rhan = (HCOUNTER *)malloc(sizeof(HCOUNTER) * diskio_no);
246 		if (diskio_rhan == NULL) {
247 			PdhCloseQuery(h_query);
248 			return -1;
249 		}
250 		diskio_whan = (HCOUNTER *)malloc(sizeof(HCOUNTER) * diskio_no);
251 		if (diskio_whan == NULL) {
252 			PdhCloseQuery(h_query);
253 			free (diskio_rhan);
254 			return -1;
255 		}
256 		for (i = 0; i < diskio_no; i++) {
257 			snprintf(tmp, sizeof(tmp), PDH_DISKIOREAD, diskio_names[i]);
258 			add_counter(tmp, &diskio_rhan[i]);
259 
260 			snprintf(tmp, sizeof(tmp), PDH_DISKIOWRITE, diskio_names[i]);
261 			add_counter(tmp, &diskio_whan[i]);
262 		}
263 	}
264 	return 0;
265 }
266 #endif
267 
268 /* Call before trying to get search results back, otherwise it'll be dead data
269  */
sg_win32_snapshot()270 int sg_win32_snapshot()
271 {
272 #ifdef WIN32
273 	PDH_STATUS pdh_status;
274 
275 	if(!is_started) {
276 		return -1;
277 	}
278 
279 	pdh_status = PdhCollectQueryData(h_query);
280 	if(pdh_status != ERROR_SUCCESS) {
281 		sg_set_error_fmt(SG_ERROR_PDHCOLLECT, NULL);
282 		return -1;
283 	}
284 #endif
285 	return 0;
286 }
287 
288 /* Must be called before any values can be read. This creates all the
289  * necessary PDH values
290  */
sg_win32_start_capture()291 int sg_win32_start_capture()
292 {
293 #ifdef WIN32
294 	PDH_STATUS pdh_status;
295 
296 	if(is_started) {
297 		return -1;
298 	}
299 
300 	pdh_status = PdhOpenQuery(NULL, 0, &h_query);
301 
302 	if(pdh_status != ERROR_SUCCESS) {
303 		char *mess = NULL;
304 		if(pdh_status == PDH_INVALID_ARGUMENT)
305 			mess = "Invalid argument o.O";
306 		else if(pdh_status == PDH_MEMORY_ALLOCATION_FAILURE)
307 			mess = "Memory allocation failure";
308 		sg_set_error_fmt(SG_ERROR_PDHOPEN, mess);
309 		return -1;
310 	}
311 	if (add_all_monitors() == -1) {
312 		return -1;
313 	}
314 
315 	is_started = 1;
316 #endif
317 	return 0;
318 }
319 
320 /* Must be called before the program exits, or to close all the capture items
321  * before opening with a call to start_capture
322  */
sg_win32_end_capture()323 void sg_win32_end_capture()
324 {
325 #ifdef WIN32
326 	int i;
327 	PDH_STATUS pdh_status;
328 
329 	if(!is_started) {
330 		return;
331 	}
332 
333 	pdh_status = PdhCloseQuery(h_query);
334 	for (i=0; i < SG_WIN32_SIZE; ++i) {
335 		PdhRemoveCounter(current_han[i]);
336 		current_han[i] = NULL;
337 	}
338 	/*free_io(&diskio_no, diskio_names, diskio_rhan, diskio_whan);
339 	free_io(&netio_no, netio_names, netio_rhan, netio_shan);*/
340 	for (i=0; i < diskio_no; ++i) {
341 		PdhRemoveCounter(diskio_rhan[i]);
342 		PdhRemoveCounter(diskio_whan[i]);
343 		free(diskio_names[i]);
344 	}
345 	free(diskio_names);
346 	free(diskio_rhan);
347 	free(diskio_whan);
348 	diskio_no = 0;
349 
350 	is_started = 0;
351 #endif
352 }
353 
354 #ifdef WIN32
355 // DllMain() is the entry-point function for this DLL.
356 
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)357 BOOL WINAPI DllMain(HINSTANCE hinstDLL, // DLL module handle
358 		    DWORD fdwReason,    // reason called
359 		    LPVOID lpvReserved) // reserved
360 {
361 	switch(fdwReason)
362 	{
363 	case DLL_PROCESS_ATTACH:
364 		if( SG_ERROR_NONE != sg_win32_start_capture() )
365 			return FALSE;
366 		break;
367 
368 	case DLL_PROCESS_DETACH:
369 		sg_win32_end_capture();
370 		break;
371 
372 #ifdef SG_WITH_THREADS
373 	case DLL_THREAD_ATTACH:
374 		break;
375 
376 	case DLL_THREAD_DETACH:
377 		sg_destroy_globals(NULL);
378 		break;
379 #endif
380 
381 	default:
382 		break;
383 	}
384 
385 	return TRUE;
386 }
387 #endif
388