1    	/*
2    	 * Utiility routines for pmlogrewrite
3    	 *
4    	 * Copyright (c) 1997-2000 Silicon Graphics, Inc.  All Rights Reserved.
5    	 * Copyright (c) 2011 Ken McDonell.  All Rights Reserved.
6    	 * 
7    	 * This program is free software; you can redistribute it and/or modify it
8    	 * under the terms of the GNU General Public License as published by the
9    	 * Free Software Foundation; either version 2 of the License, or (at your
10   	 * option) any later version.
11   	 * 
12   	 * This program is distributed in the hope that it will be useful, but
13   	 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14   	 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15   	 * for more details.
16   	 * 
17   	 * You should have received a copy of the GNU General Public License along
18   	 * with this program; if not, write to the Free Software Foundation, Inc.,
19   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20   	 */
21   	
22   	#include "pmapi.h"
23   	#include "impl.h"
24   	#include "logger.h"
25   	#include <assert.h>
26   	#include <ctype.h>
27   	#include <sys/stat.h>
28   	#include <unistd.h>
29   	
30   	void
31   	yywarn(char *s)
32   	{
33   	    fprintf(stderr, "Warning [%s, line %d]\n%s\n", configfile, lineno, s);
34   	}
35   	
36   	void
37   	yyerror(char *s)
38   	{
39   	    fprintf(stderr, "Specification error in configuration file (%s)\n",
40   		    configfile);
41   	    fprintf(stderr, "[line %d] %s\n", lineno, s);
42   	    exit(1);
43   	}
44   	
45   	void
46   	yysemantic(char *s)
47   	{
48   	    fprintf(stderr, "Semantic error in configuration file (%s)\n",
49   		    configfile);
50   	    fprintf(stderr, "%s\n", s);
51   	    exit(1);
52   	}
53   	
54   	
55   	/*
56   	 * Walk a hash list ... mode is W_START ... W_NEXT ... W_NEXT ...
57   	 */
58   	__pmHashNode *
59   	__pmHashWalk(__pmHashCtl *hcp, int mode)
60   	{
61   	    static int		hash_idx;
62   	    static __pmHashNode	*next;
63   	    __pmHashNode	*this;
64   	
65   	    if (mode == W_START) {
66   		hash_idx = 0;
67   		next = hcp->hash[0];
68   	    }
69   	
70   	    while (next == NULL) {
71   		hash_idx++;
72   		if (hash_idx >= hcp->hsize)
73   		    return NULL;
74   		next = hcp->hash[hash_idx];
75   	    }
76   	
77   	    this = next;
78   	    next = next->next;
79   	
80   	    return this;
81   	}
82   	
83   	/*
84   	 * instance name matching ... return
85   	 *  0 for no match
86   	 *  1 for match to first space
87   	 *  2 for complete match
88   	 * -1 if either name is empty or NULL
89   	 */
90   	int
91   	inst_name_eq(const char *p, const char *q)
92   	{
93   	    if (p == NULL || *p == '\0')
94   		return -1;
95   	    if (q == NULL || *q == '\0')
96   		return -1;
97   	
98   	    for ( ; ; p++, q++) {
99   		if (*p == '\0' && *q == '\0')
100  		    return 2;
101  		if (*p == '\0' || *p == ' ') {
102  		    if (*q == '\0' || *q == ' ')
103  			return 1;
104  		    break;
105  		}
106  		if (*q == '\0' || *q == ' ') {
107  		    if (*p == '\0' || *p == ' ')
108  			return 1;
109  		    break;
110  		}
111  		if (*p != *q)
112  		    break;
113  	    }
114  	    return 0;
115  	}
116  	
117  	/*
118  	 * Rename all the physical archive files with basename of old to
119  	 * a basename of new.
120  	 *
121  	 * If _any_ error occurs, don't make any changes.
122  	 *
123  	 * Note: does not handle compressed versions of files.
124  	 *
125  	 * TODO - need global locking for PCP 4.0 version if this is promoted
126  	 *        to libpcp
127  	 */
128  	int
129  	__pmLogRename(const char *old, const char *new)
130  	{
131  	    int			sts;
132  	    int			nfound = 0;
133  	    char		**found = NULL;
134  	    char		*dname;
135  	    char		*obase;
136  	    char		path[MAXPATHLEN+1];
137  	    char		opath[MAXPATHLEN+1];
138  	    char		npath[MAXPATHLEN+1];
139  	    DIR			*dirp;
140  	    struct dirent	*dp;
141  	
142  	    strncpy(path, old, sizeof(path));
143  	    dname = dirname(path);
144  	
145  	    if ((dirp = opendir(dname)) == NULL)
146  		return -oserror();
147  	
148  	    strncpy(path, old, sizeof(path));
149  	    obase = basename(path);
150  	
151  	    for ( ; ; ) {
152  		setoserror(0);
153  		if ((dp = readdir(dirp)) == NULL)
154  		    break;
155  		if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
156  		    continue;
157  		if (strncmp(obase, dp->d_name, strlen(obase)) == 0) {
158  		    /*
159  		     * match the base part of the old archive name, now check
160  		     * for meta or index or a valid volume number
161  		     */
162  		    char	*p = &dp->d_name[strlen(obase)];
163  		    int		want = 0;
164  		    if (strcmp(p, ".meta") == 0)
165  			want = 1;
166  		    else if (strcmp(p, ".index") == 0)
167  			want = 1;
168  		    else if (*p == '.' && isdigit(p[1])) {
169  			char	*endp;
170  			long	vol;
171  			vol = strtol(&p[1], &endp, 10);
172  			if (vol >= 0 && *endp == '\0')
173  			    want = 1;
174  		    }
175  		    if (want) {
176  			struct stat	stbuf;
177  			snprintf(opath, sizeof(opath), "%s%s", old, p);
178  			snprintf(npath, sizeof(npath), "%s%s", new, p);
179  			if (stat(npath, &stbuf) == 0) {
180  			    fprintf(stderr, "__pmLogRename: destination file %s already exists\n", npath);
181  			    goto revert;
182  			}
183  			if (rename(opath, npath) == -1) {
184  			    fprintf(stderr, "__pmLogRename: rename %s -> %s failed: %s\n", opath, npath, pmErrStr(-oserror()));
185  			    goto revert;
186  			}
187  			nfound++;
188  			found = (char **)realloc(found, nfound*sizeof(found[0]));
189  			if (found == NULL) {
190  			    __pmNoMem("__pmLogRename: realloc", nfound*sizeof(found[0]), PM_RECOV_ERR);
191  			    abandon();
192  			}
193  			if ((found[nfound-1] = strdup(p)) == NULL) {
194  			    __pmNoMem("__pmLogRename: strdup", strlen(p)+1, PM_RECOV_ERR);
195  			    abandon();
196  			}
197  	#ifdef PCP_DEBUG
198  			if (pmDebug & DBG_TRACE_LOG)
199  			    fprintf(stderr, "__pmLogRename: %s -> %s\n", opath, npath);
200  	#endif
201  		    }
202  		}
203  	    }
204  	
205  	    if ((sts = oserror()) != 0) {
206  		fprintf(stderr, "__pmLogRename: readdir for %s failed: %s\n", dname, pmErrStr(-sts));
207  		goto revert;
208  	    }
209  	
210  	    closedir(dirp);
211  	    sts = 0;
212  	    goto cleanup;
213  	
214  	revert:
215  	    while (nfound > 0) {
216  		snprintf(opath, sizeof(opath), "%s%s", old, found[nfound-1]);
217  		snprintf(npath, sizeof(npath), "%s%s", new, found[nfound-1]);
218  		if (rename(npath, opath) == -1) {
219  		    fprintf(stderr, "__pmLogRename: arrgh trying to revert rename %s -> %s failed: %s\n", npath, opath, pmErrStr(-oserror()));
220  		}
221  	#ifdef PCP_DEBUG
222  		if (pmDebug & DBG_TRACE_LOG)
223  		    fprintf(stderr, "__pmLogRename: revert %s <- %s\n", opath, npath);
224  	#endif
225  		nfound--;
226  	    }
227  	    sts = PM_ERR_GENERIC;
228  	
229  	cleanup:
230  	    while (nfound > 0) {
231  		free(found[nfound-1]);
232  		nfound--;
233  	    }
234  	    if (found != NULL)
235  		free(found);
236  	
237  	    return sts;
238  	}
239  	
240  	/*
241  	 * Remove all the physical archive files with basename of base.
242  	 *
243  	 * Note: does not handle compressed versions of files.
244  	 *
245  	 * TODO - need global locking for PCP 4.0 version if this is promoted
246  	 *        to libpcp
247  	 */
248  	int
249  	__pmLogRemove(const char *name)
250  	{
251  	    int			sts;
252  	    int			nfound = 0;
253  	    char		*dname;
254  	    char		*base;
255  	    char		path[MAXPATHLEN+1];
256  	    DIR			*dirp;
257  	    struct dirent	*dp;
258  	
259  	    strncpy(path, name, sizeof(path));
260  	    dname = strdup(dirname(path));
261  	    if (dname == NULL) {
262  		__pmNoMem("__pmLogRemove: dirname strdup", strlen(dirname(path))+1, PM_RECOV_ERR);
263  		abandon();
264  	    }
265  	
266  	    if ((dirp = opendir(dname)) == NULL)
267  		return -oserror();
268  	
269  	    strncpy(path, name, sizeof(path));
270  	    base = strdup(basename(path));
Event var_compare_op: Comparing "base" to null implies that "base" might be null.
Also see events: [var_deref_model]
At conditional (1): "base == NULL": Taking true branch.
271  	    if (base == NULL) {
272  		__pmNoMem("__pmLogRemove: basename strdup", strlen(basename(path))+1, PM_RECOV_ERR);
273  		abandon();
274  	    }
275  	
At conditional (2): "true": Taking true branch.
276  	    for ( ; ; ) {
277  		setoserror(0);
At conditional (3): "(dp = readdir(dirp)) == NULL": Taking false branch.
278  		if ((dp = readdir(dirp)) == NULL)
279  		    break;
At conditional (4): "strcmp(dp->d_name, ".") == 0": Taking false branch.
At conditional (5): "strcmp(dp->d_name, "..") == 0": Taking false branch.
280  		if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
281  		    continue;
Event var_deref_model: Passing null variable "base" to function "strlen", which dereferences it. (The dereference is assumed on the basis of the 'nonnull' parameter attribute.)
Also see events: [var_compare_op]
282  		if (strncmp(base, dp->d_name, strlen(base)) == 0) {
283  		    /*
284  		     * match the base part of the old archive name, now check
285  		     * for meta or index or a valid volume number
286  		     */
287  		    char	*p = &dp->d_name[strlen(base)];
288  		    int		want = 0;
289  		    if (strcmp(p, ".meta") == 0)
290  			want = 1;
291  		    else if (strcmp(p, ".index") == 0)
292  			want = 1;
293  		    else if (*p == '.' && isdigit(p[1])) {
294  			char	*endp;
295  			long	vol;
296  			vol = strtol(&p[1], &endp, 10);
297  			if (vol >= 0 && *endp == '\0')
298  			    want = 1;
299  		    }
300  		    if (want) {
301  			snprintf(path, sizeof(path), "%s%s", name, p);
302  			unlink(path);
303  			nfound++;
304  	#ifdef PCP_DEBUG
305  			if (pmDebug & DBG_TRACE_LOG)
306  			    fprintf(stderr, "__pmLogRemove: %s\n", path);
307  	#endif
308  		    }
309  		}
310  	    }
311  	
312  	    if ((sts = oserror()) != 0) {
313  		fprintf(stderr, "__pmLogRemove: readdir for %s failed: %s\n", dname, pmErrStr(-sts));
314  		sts = -sts;
315  	    }
316  	    else
317  		sts = nfound;
318  	
319  	    closedir(dirp);
320  	
321  	    return sts;
322  	}