Annotation of src/usr.bin/cvs/root.c, Revision 1.12
1.12 ! deraadt 1: /* $OpenBSD: root.c,v 1.11 2004/08/31 11:54:35 jfb Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: *
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27: #include <sys/types.h>
28:
29: #include <stdlib.h>
30: #include <stdio.h>
31: #include <unistd.h>
32: #include <err.h>
33: #include <errno.h>
34: #include <string.h>
35: #include <paths.h>
36:
37: #include "cvs.h"
38: #include "log.h"
1.10 jfb 39: #include "proto.h"
1.1 jfb 40:
41:
42: extern char *cvs_rootstr;
43:
44:
45: /* keep these ordered with the defines */
46: const char *cvs_methods[] = {
47: "",
48: "local",
49: "ssh",
50: "pserver",
51: "kserver",
52: "gserver",
53: "ext",
54: "fork",
55: };
56:
57: #define CVS_NBMETHODS (sizeof(cvs_methods)/sizeof(cvs_methods[0]))
1.6 jfb 58:
59: /*
60: * CVSROOT cache
61: *
62: * Whenever cvsroot_parse() gets called for a specific string, it first
63: * checks in the cache to see if there is already a parsed version of the
64: * same string and returns a pointer to it in case one is found (it also
65: * increases the reference count). Otherwise, it does the parsing and adds
66: * the result to the cache for future hits.
67: */
68:
69: static struct cvsroot **cvs_rcache = NULL;
70: static u_int cvs_rcsz = 0;
71:
1.1 jfb 72:
73:
74: /*
75: * cvsroot_parse()
76: *
77: * Parse a CVS root string (as found in CVS/Root files or the CVSROOT
78: * environment variable) and store the fields in a dynamically
79: * allocated cvs_root structure. The format of the string is as follows:
1.6 jfb 80: * :method:[[user[:pass]@host]:path
1.1 jfb 81: * Returns a pointer to the allocated information on success, or NULL
82: * on failure.
83: */
84:
85: struct cvsroot*
86: cvsroot_parse(const char *str)
87: {
88: u_int i;
89: char *cp, *sp, *pp;
1.6 jfb 90: void *tmp;
1.1 jfb 91: struct cvsroot *root;
92:
1.6 jfb 93: for (i = 0; i < cvs_rcsz; i++) {
94: if (strcmp(str, cvs_rcache[i]->cr_str) == 0) {
95: cvs_rcache[i]->cr_ref++;
96: return (cvs_rcache[i]);
97: }
98: }
99:
1.1 jfb 100: root = (struct cvsroot *)malloc(sizeof(*root));
101: if (root == NULL) {
102: cvs_log(LP_ERRNO, "failed to allocate CVS root data");
103: return (NULL);
104: }
1.4 jfb 105: memset(root, 0, sizeof(*root));
1.6 jfb 106: root->cr_ref = 2;
1.1 jfb 107: root->cr_method = CVS_METHOD_NONE;
1.10 jfb 108: CVS_RSTVR(root);
109:
110: /* enable the most basic commands at least */
111: CVS_SETVR(root, CVS_REQ_VALIDREQ);
112: CVS_SETVR(root, CVS_REQ_VALIDRESP);
1.1 jfb 113:
1.6 jfb 114: root->cr_str = strdup(str);
115: if (root->cr_str == NULL) {
116: free(root);
117: return (NULL);
118: }
1.1 jfb 119: root->cr_buf = strdup(str);
120: if (root->cr_buf == NULL) {
121: cvs_log(LP_ERRNO, "failed to copy CVS root");
1.6 jfb 122: cvsroot_free(root);
1.1 jfb 123: return (NULL);
124: }
125:
126: sp = root->cr_buf;
127: cp = root->cr_buf;
128: if (*sp == ':') {
129: sp++;
130: cp = strchr(sp, ':');
131: if (cp == NULL) {
132: cvs_log(LP_ERR, "failed to parse CVSROOT: "
133: "unterminated method");
1.6 jfb 134: cvsroot_free(root);
1.1 jfb 135: return (NULL);
136: }
137: *(cp++) = '\0';
138:
139: for (i = 0; i < CVS_NBMETHODS; i++) {
140: if (strcmp(sp, cvs_methods[i]) == 0) {
141: root->cr_method = i;
142: break;
143: }
144: }
145: }
146:
147: /* find the start of the actual path */
148: sp = strchr(cp, '/');
149: if (sp == NULL) {
150: cvs_log(LP_ERR, "no path specification in CVSROOT");
1.6 jfb 151: cvsroot_free(root);
1.1 jfb 152: return (NULL);
153: }
154:
155: root->cr_dir = sp;
156: if (sp == cp) {
157: if (root->cr_method == CVS_METHOD_NONE)
158: root->cr_method = CVS_METHOD_LOCAL;
159: /* stop here, it's just a path */
160: return (root);
161: }
162:
163: if (*(sp - 1) != ':') {
164: cvs_log(LP_ERR, "missing host/path delimiter in CVS root");
1.6 jfb 165: cvsroot_free(root);
1.1 jfb 166: return (NULL);
167: }
168: *(sp - 1) = '\0';
169:
170: /*
171: * looks like we have more than just a directory path, so
172: * attempt to split it into user and host parts
173: */
174: sp = strchr(cp, '@');
175: if (sp != NULL) {
176: *(sp++) = '\0';
177:
178: /* password ? */
179: pp = strchr(cp, ':');
180: if (pp != NULL) {
181: *(pp++) = '\0';
182: root->cr_pass = pp;
183: }
184:
185: root->cr_user = cp;
1.12 ! deraadt 186: } else
1.11 jfb 187: sp = cp;
1.1 jfb 188:
189: pp = strchr(sp, ':');
190: if (pp != NULL) {
191: *(pp++) = '\0';
192: root->cr_port = (u_int)strtol(pp, &cp, 10);
193: if (*cp != '\0' || root->cr_port > 65535) {
194: cvs_log(LP_ERR,
195: "invalid port specification in CVSROOT");
1.6 jfb 196: cvsroot_free(root);
1.1 jfb 197: return (NULL);
198: }
199:
200: }
201:
202: root->cr_host = sp;
203:
204: if (root->cr_method == CVS_METHOD_NONE) {
205: /* no method found from start of CVSROOT, guess */
206: if (root->cr_host != NULL)
207: root->cr_method = CVS_METHOD_SERVER;
208: else
209: root->cr_method = CVS_METHOD_LOCAL;
210: }
211:
1.6 jfb 212: /* add to the cache */
213: tmp = realloc(cvs_rcache, (cvs_rcsz + 1) * sizeof(struct cvsroot *));
214: if (tmp == NULL) {
215: /* just forget about the cache and return anyways */
216: root->cr_ref--;
1.12 ! deraadt 217: } else {
1.6 jfb 218: cvs_rcache = (struct cvsroot **)tmp;
219: cvs_rcache[cvs_rcsz++] = root;
220: }
221:
1.1 jfb 222: return (root);
223: }
224:
225:
226: /*
227: * cvsroot_free()
228: *
229: * Free a CVSROOT structure previously allocated and returned by
230: * cvsroot_parse().
231: */
232:
233: void
234: cvsroot_free(struct cvsroot *root)
235: {
1.6 jfb 236: root->cr_ref--;
237: if (root->cr_ref == 0) {
238: if (root->cr_str != NULL)
239: free(root->cr_str);
240: if (root->cr_buf != NULL)
241: free(root->cr_buf);
1.9 jfb 242: if (root->cr_version != NULL)
243: free(root->cr_version);
1.6 jfb 244: free(root);
245: }
1.1 jfb 246: }
247:
248:
249: /*
250: * cvsroot_get()
251: *
252: * Get the CVSROOT information for a specific directory <dir>. The
253: * value is taken from one of 3 possible sources (in order of precedence):
254: *
255: * 1) the `-d' command-line option
256: * 2) the CVS/Root file found in checked-out trees
257: * 3) the CVSROOT environment variable
258: */
259:
260: struct cvsroot*
261: cvsroot_get(const char *dir)
262: {
263: size_t len;
1.5 jfb 264: char rootpath[MAXPATHLEN], *rootstr, line[128];
1.1 jfb 265: FILE *fp;
266:
267: if (cvs_rootstr != NULL)
268: return cvsroot_parse(cvs_rootstr);
269:
270: snprintf(rootpath, sizeof(rootpath), "%s/" CVS_PATH_ROOTSPEC, dir);
271: fp = fopen(rootpath, "r");
272: if (fp == NULL) {
273: if (errno == ENOENT) {
274: /* try env as a last resort */
275: if ((rootstr = getenv("CVSROOT")) != NULL)
276: return cvsroot_parse(rootstr);
277: else
278: return (NULL);
1.12 ! deraadt 279: } else {
1.1 jfb 280: cvs_log(LP_ERRNO, "failed to open CVS/Root");
281: return (NULL);
282: }
283: }
284:
1.5 jfb 285: if (fgets(line, sizeof(line), fp) == NULL) {
1.1 jfb 286: cvs_log(LP_ERR, "failed to read CVSROOT line from CVS/Root");
287: (void)fclose(fp);
1.5 jfb 288: return (NULL);
1.1 jfb 289: }
1.5 jfb 290: (void)fclose(fp);
1.1 jfb 291:
1.5 jfb 292: len = strlen(line);
293: if (len == 0) {
294: cvs_log(LP_WARN, "empty CVS/Root file");
1.12 ! deraadt 295: } else if (line[len - 1] == '\n')
1.5 jfb 296: line[--len] = '\0';
1.1 jfb 297:
1.5 jfb 298: return cvsroot_parse(line);
1.1 jfb 299: }