1 /*
2  * Environment handling routines
3  *
4  * Copyright 2020 by Gray Watson
5  *
6  * This file is part of the dmalloc package.
7  *
8  * Permission to use, copy, modify, and distribute this software for
9  * any purpose and without fee is hereby granted, provided that the
10  * above copyright notice and this permission notice appear in all
11  * copies, and that the name of Gray Watson not be used in advertising
12  * or publicity pertaining to distribution of the document or software
13  * without specific, written prior permission.
14  *
15  * Gray Watson makes no representations about the suitability of the
16  * software described herein for any purpose.  It is provided "as is"
17  * without express or implied warranty.
18  *
19  * The author may be contacted via http://dmalloc.com/
20  */
21 
22 /*
23  * This file contains short routine(s) to process the environment
24  * variable(s) used by the library to get the runtime option(s).
25  */
26 
27 #define DMALLOC_DISABLE
28 
29 #if HAVE_STDLIB_H
30 # include <stdlib.h>
31 #endif
32 #if HAVE_STRING_H
33 # include <string.h>
34 #endif
35 #if HAVE_UNISTD_H
36 # include <unistd.h>				/* for getpid */
37 #endif
38 
39 #include "conf.h"
40 #include "dmalloc.h"
41 
42 #include "append.h"
43 #include "compat.h"
44 #include "dmalloc_loc.h"
45 #include "debug_tok.h"
46 #include "env.h"
47 #include "error.h"
48 
49 /*
50  * Environmental labels.
51  *
52  * NOTE: the decision has been made _not_ to do the sizeof() hack for
53  * portability reasons.
54  */
55 #define ADDRESS_LABEL		"addr"
56 #define DEBUG_LABEL		"debug"
57 #define INTERVAL_LABEL		"inter"
58 #define LOCK_ON_LABEL		"lockon"
59 #define LOGFILE_LABEL		"log"
60 #define START_LABEL		"start"
61 #define LIMIT_LABEL		"limit"
62 
63 #define ASSIGNMENT_CHAR		'='
64 
65 /* local variables */
66 static	char		log_path[512]	= { '\0' }; /* storage for env path */
67 static	char		start_file[512] = { '\0' }; /* file to start at */
68 
69 /****************************** local utilities ******************************/
70 
71 /*
72  * Hexadecimal STR to long translation
73  */
hex_to_uint(const char * str)74 static	unsigned int	hex_to_uint(const char *str)
75 {
76   unsigned int	ret;
77 
78   /* strip off spaces */
79   for (; *str == ' ' || *str == '\t'; str++) {
80   }
81 
82   /* skip a leading 0[xX] */
83   if (*str == '0' && (*(str + 1) == 'x' || *(str + 1) == 'X')) {
84     str += 2;
85   }
86 
87   for (ret = 0;; str++) {
88     if (*str >= '0' && *str <= '9') {
89       ret = ret * 16 + (*str - '0');
90     }
91     else if (*str >= 'a' && *str <= 'f') {
92       ret = ret * 16 + (*str - 'a' + 10);
93     }
94     else if (*str >= 'A' && *str <= 'F') {
95       ret = ret * 16 + (*str - 'A' + 10);
96     }
97     else {
98       break;
99     }
100   }
101 
102   return ret;
103 }
104 
105 /*
106  * Hexadecimal STR to address translation
107  */
hex_to_address(const char * str)108 static	DMALLOC_PNT	hex_to_address(const char *str)
109 {
110   PNT_ARITH_TYPE	ret;
111 
112   /* strip off spaces */
113   for (; *str == ' ' || *str == '\t'; str++) {
114   }
115 
116   /* skip a leading 0[xX] */
117   if (*str == '0' && (*(str + 1) == 'x' || *(str + 1) == 'X')) {
118     str += 2;
119   }
120 
121   for (ret = 0;; str++) {
122     if (*str >= '0' && *str <= '9') {
123       ret = ret * 16 + (*str - '0');
124     }
125     else if (*str >= 'a' && *str <= 'f') {
126       ret = ret * 16 + (*str - 'a' + 10);
127     }
128     else if (*str >= 'A' && *str <= 'F') {
129       ret = ret * 16 + (*str - 'A' + 10);
130     }
131     else {
132       break;
133     }
134   }
135 
136   return (DMALLOC_PNT)ret;
137 }
138 
139 /***************************** exported routines *****************************/
140 
141 /*
142  * Break up ADDR_ALL into ADDR_P and ADDR_COUNT_P
143  */
_dmalloc_address_break(const char * addr_all,DMALLOC_PNT * addr_p,unsigned long * addr_count_p)144 void	_dmalloc_address_break(const char *addr_all, DMALLOC_PNT *addr_p,
145 			       unsigned long *addr_count_p)
146 {
147   char	*colon_p;
148 
149   SET_POINTER(addr_p, hex_to_address(addr_all));
150   if (addr_count_p != NULL) {
151     colon_p = strchr(addr_all, ':');
152     if (colon_p != NULL) {
153       *addr_count_p = loc_atoul(colon_p + 1);
154     }
155   }
156 }
157 
158 /*
159  * Break up START_ALL into SFILE_P, SLINE_P, and SCOUNT_P
160  */
_dmalloc_start_break(char * start_all,char ** start_file_p,int * start_line_p,unsigned long * start_iter_p,unsigned long * start_size_p)161 void	_dmalloc_start_break(char *start_all, char **start_file_p,
162 			     int *start_line_p, unsigned long *start_iter_p,
163 			     unsigned long *start_size_p)
164 {
165   char	*start_p;
166 
167   start_p = strchr(start_all, ':');
168   if (start_p != NULL) {
169     (void)strncpy(start_file, start_all, sizeof(start_file));
170     start_file[sizeof(start_file) - 1] = '\0';
171     SET_POINTER(start_file_p, start_file);
172     start_p = start_file + (start_p - start_all);
173     *start_p = '\0';
174     SET_POINTER(start_line_p, atoi(start_p + 1));
175     SET_POINTER(start_iter_p, 0);
176     SET_POINTER(start_size_p, 0);
177   }
178   else if (start_all[0] == 's') {
179     SET_POINTER(start_file_p, NULL);
180     SET_POINTER(start_line_p, 0);
181     SET_POINTER(start_iter_p, 0);
182     SET_POINTER(start_size_p, loc_atoul(start_all + 1));
183   }
184   else {
185     SET_POINTER(start_file_p, NULL);
186     SET_POINTER(start_line_p, 0);
187     if (start_all[0] == 'c') {
188       SET_POINTER(start_iter_p, loc_atoul(start_all + 1));
189     }
190     else {
191       SET_POINTER(start_iter_p, loc_atoul(start_all));
192     }
193     SET_POINTER(start_size_p, 0);
194   }
195 }
196 
197 /*
198  * Process the values of dmalloc environ variable(s) from ENVIRON
199  * string.
200  */
_dmalloc_environ_process(const char * env_str,DMALLOC_PNT * addr_p,unsigned long * addr_count_p,unsigned int * debug_p,unsigned long * interval_p,int * lock_on_p,char ** logpath_p,char ** start_file_p,int * start_line_p,unsigned long * start_iter_p,unsigned long * start_size_p,unsigned long * limit_p)201 void	_dmalloc_environ_process(const char *env_str, DMALLOC_PNT *addr_p,
202 				 unsigned long *addr_count_p,
203 				 unsigned int *debug_p,
204 				 unsigned long *interval_p, int *lock_on_p,
205 				 char **logpath_p, char **start_file_p,
206 				 int *start_line_p,
207 				 unsigned long *start_iter_p,
208 				 unsigned long *start_size_p,
209 				 unsigned long *limit_p)
210 {
211   char		*env_p, *this_p;
212   char		buf[1024];
213   int		len, done_b = 0;
214   unsigned int	flags = 0;
215   attr_t	*attr_p;
216 
217   SET_POINTER(addr_p, NULL);
218   SET_POINTER(addr_count_p, 0);
219   SET_POINTER(debug_p, 0);
220   SET_POINTER(interval_p, 0);
221   SET_POINTER(lock_on_p, 0);
222   SET_POINTER(logpath_p, NULL);
223   SET_POINTER(start_file_p, NULL);
224   SET_POINTER(start_line_p, 0);
225   SET_POINTER(start_iter_p, 0);
226   SET_POINTER(start_size_p, 0);
227   SET_POINTER(limit_p, 0);
228 
229   /* make a copy */
230   (void)strncpy(buf, env_str, sizeof(buf));
231   buf[sizeof(buf) - 1] = '\0';
232 
233   /* handle each of tokens, in turn */
234   for (env_p = buf, this_p = buf; ! done_b; env_p++, this_p = env_p) {
235 
236     /* find the comma of end */
237     for (;; env_p++) {
238       if (*env_p == '\0') {
239 	done_b = 1;
240 	break;
241       }
242       if (*env_p == ',' && (env_p == buf || *(env_p - 1) != '\\')) {
243 	break;
244       }
245     }
246 
247     /* should we strip ' ' or '\t' here? */
248 
249     if (this_p == env_p) {
250       continue;
251     }
252 
253     *env_p = '\0';
254 
255     len = strlen(ADDRESS_LABEL);
256     if (strncmp(this_p, ADDRESS_LABEL, len) == 0
257 	&& *(this_p + len) == ASSIGNMENT_CHAR) {
258       this_p += len + 1;
259       _dmalloc_address_break(this_p, addr_p, addr_count_p);
260       continue;
261     }
262 
263     len = strlen(DEBUG_LABEL);
264     if (strncmp(this_p, DEBUG_LABEL, len) == 0
265 	&& *(this_p + len) == ASSIGNMENT_CHAR) {
266       this_p += len + 1;
267       SET_POINTER(debug_p, hex_to_uint(this_p));
268       continue;
269     }
270 
271     len = strlen(INTERVAL_LABEL);
272     if (strncmp(this_p, INTERVAL_LABEL, len) == 0
273 	&& *(this_p + len) == ASSIGNMENT_CHAR) {
274       this_p += len + 1;
275       SET_POINTER(interval_p, loc_atoul(this_p));
276       continue;
277     }
278 
279     len = strlen(LOCK_ON_LABEL);
280     if (strncmp(this_p, LOCK_ON_LABEL, len) == 0
281 	&& *(this_p + len) == ASSIGNMENT_CHAR) {
282       this_p += len + 1;
283       SET_POINTER(lock_on_p, atoi(this_p));
284       continue;
285     }
286 
287     /* get the dmalloc logfile name into a holding variable */
288     len = strlen(LOGFILE_LABEL);
289     if (strncmp(this_p, LOGFILE_LABEL, len) == 0
290 	&& *(this_p + len) == ASSIGNMENT_CHAR) {
291       this_p += len + 1;
292       (void)strncpy(log_path, this_p, sizeof(log_path));
293       log_path[sizeof(log_path) - 1] = '\0';
294       SET_POINTER(logpath_p, log_path);
295       continue;
296     }
297 
298     /*
299      * start checking the heap after X iterations OR
300      * start at a file:line combination
301      */
302     len = strlen(START_LABEL);
303     if (strncmp(this_p, START_LABEL, len) == 0
304 	&& *(this_p + len) == ASSIGNMENT_CHAR) {
305       this_p += len + 1;
306       _dmalloc_start_break(this_p, start_file_p, start_line_p, start_iter_p,
307 			   start_size_p);
308       continue;
309     }
310 
311     /* set the memory limit to the library */
312     len = strlen(LIMIT_LABEL);
313     if (strncmp(this_p, LIMIT_LABEL, len) == 0
314 	&& *(this_p + len) == ASSIGNMENT_CHAR) {
315       this_p += len + 1;
316       SET_POINTER(limit_p, loc_atoul(this_p));
317       continue;
318     }
319 
320     /* need to check the short/long debug options */
321     for (attr_p = attributes; attr_p->at_string != NULL; attr_p++) {
322       if (strcmp(this_p, attr_p->at_string) == 0) {
323 	flags |= attr_p->at_value;
324 	break;
325       }
326     }
327     if (attr_p->at_string != NULL) {
328       continue;
329     }
330   }
331 
332   /* append the token settings to the debug setting */
333   if (debug_p != NULL) {
334     if (*debug_p == 0) {
335       *debug_p = flags;
336     }
337     else {
338       *debug_p |= flags;
339     }
340   }
341 }
342 
343 /*
344  * Set dmalloc environ variable(s) with the values (maybe SHORT debug
345  * info) into BUF.
346  */
_dmalloc_environ_set(char * buf,const int buf_size,const int long_tokens_b,const DMALLOC_PNT address,const unsigned long addr_count,const unsigned int debug,const unsigned long interval,const int lock_on,const char * logpath,const char * start_file_p,const int start_line,const unsigned long start_iter,const unsigned long start_size,const unsigned long limit_val)347 void	_dmalloc_environ_set(char *buf, const int buf_size,
348 			     const int long_tokens_b,
349 			     const DMALLOC_PNT address,
350 			     const unsigned long addr_count,
351 			     const unsigned int debug,
352 			     const unsigned long interval, const int lock_on,
353 			     const char *logpath, const char *start_file_p,
354 			     const int start_line,
355 			     const unsigned long start_iter,
356 			     const unsigned long start_size,
357 			     const unsigned long limit_val)
358 {
359   char	*buf_p = buf, *bounds_p = buf + buf_size;
360 
361   if (debug > 0) {
362     if (long_tokens_b) {
363       attr_t	*attr_p;
364 
365       for (attr_p = attributes; attr_p->at_string != NULL; attr_p++) {
366 	if (debug & attr_p->at_value) {
367 	  buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s,",
368 				attr_p->at_string);
369 	}
370       }
371     }
372     else {
373       buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%c%#x,",
374 			    DEBUG_LABEL, ASSIGNMENT_CHAR, debug);
375     }
376   }
377   if (address != NULL) {
378     if (addr_count > 0) {
379       buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%c%p:%lu,",
380 			    ADDRESS_LABEL, ASSIGNMENT_CHAR, address, addr_count);
381     }
382     else {
383       buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%c%p,",
384 			    ADDRESS_LABEL, ASSIGNMENT_CHAR, address);
385     }
386   }
387   if (interval > 0) {
388     buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%c%lu,",
389 			  INTERVAL_LABEL, ASSIGNMENT_CHAR, interval);
390   }
391   if (lock_on > 0) {
392     buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%c%d,",
393 			  LOCK_ON_LABEL, ASSIGNMENT_CHAR, lock_on);
394   }
395   if (logpath != NULL) {
396     buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%c%s,",
397 			  LOGFILE_LABEL, ASSIGNMENT_CHAR, logpath);
398   }
399   if (start_file_p != NULL) {
400     if (start_line > 0) {
401       buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%c%s:%d,",
402 			    START_LABEL, ASSIGNMENT_CHAR, start_file_p,
403 			    start_line);
404     }
405     else {
406       buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%c%s,",
407 			    START_LABEL, ASSIGNMENT_CHAR, start_file_p);
408     }
409   }
410   else if (start_iter > 0) {
411     /* NOTE: there is an 'c' (for count) before the iter variable here */
412     buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%cc%lu,",
413 			  START_LABEL, ASSIGNMENT_CHAR, start_iter);
414   }
415   else if (start_size > 0) {
416     /* NOTE: there is an 's' before the size variable here */
417     buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%cs%lu,",
418 			  START_LABEL, ASSIGNMENT_CHAR, start_size);
419   }
420   if (limit_val > 0) {
421     buf_p += loc_snprintf(buf_p, bounds_p - buf_p, "%s%c%lu,",
422 			  LIMIT_LABEL, ASSIGNMENT_CHAR, limit_val);
423   }
424 
425   /* cut off the last comma */
426   if (buf_p > buf) {
427     buf_p--;
428   }
429 
430   *buf_p = '\0';
431 }
432