Annotation of src/usr.bin/cvs/root.c, Revision 1.30
1.30 ! xsa 1: /* $OpenBSD: root.c,v 1.29 2006/01/25 08:15:05 xsa Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.13 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.13 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.13 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.13 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 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
1.13 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
1.28 xsa 27: #include "includes.h"
1.1 jfb 28:
29: #include "cvs.h"
30: #include "log.h"
1.10 jfb 31: #include "proto.h"
1.1 jfb 32:
33:
34: extern char *cvs_rootstr;
35:
36:
37: /* keep these ordered with the defines */
38: const char *cvs_methods[] = {
39: "",
40: "local",
41: "ssh",
42: "pserver",
43: "kserver",
44: "gserver",
45: "ext",
46: "fork",
47: };
48:
1.20 xsa 49: #define CVS_NBMETHODS (sizeof(cvs_methods)/sizeof(cvs_methods[0]))
1.6 jfb 50:
51: /*
52: * CVSROOT cache
53: *
54: * Whenever cvsroot_parse() gets called for a specific string, it first
55: * checks in the cache to see if there is already a parsed version of the
56: * same string and returns a pointer to it in case one is found (it also
57: * increases the reference count). Otherwise, it does the parsing and adds
58: * the result to the cache for future hits.
59: */
1.22 joris 60: static TAILQ_HEAD(, cvsroot) cvs_rcache = TAILQ_HEAD_INITIALIZER(cvs_rcache);
1.25 joris 61: static void cvsroot_free(struct cvsroot *);
1.1 jfb 62:
63: /*
64: * cvsroot_parse()
65: *
66: * Parse a CVS root string (as found in CVS/Root files or the CVSROOT
67: * environment variable) and store the fields in a dynamically
68: * allocated cvs_root structure. The format of the string is as follows:
1.6 jfb 69: * :method:[[user[:pass]@host]:path
1.1 jfb 70: * Returns a pointer to the allocated information on success, or NULL
71: * on failure.
72: */
1.21 xsa 73: struct cvsroot *
1.1 jfb 74: cvsroot_parse(const char *str)
75: {
76: u_int i;
77: char *cp, *sp, *pp;
78: struct cvsroot *root;
79:
1.22 joris 80: /*
81: * Look if we have it in cache, if we found it add it to the cache
82: * at the first position again.
83: */
84: TAILQ_FOREACH(root, &cvs_rcache, root_cache) {
85: if (strcmp(str, root->cr_str) == 0) {
86: TAILQ_REMOVE(&cvs_rcache, root, root_cache);
87: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
88: root->cr_ref++;
89: return (root);
1.6 jfb 90: }
91: }
92:
1.26 joris 93: root = (struct cvsroot *)xmalloc(sizeof(*root));
1.4 jfb 94: memset(root, 0, sizeof(*root));
1.15 jfb 95: root->cr_ref = 1;
1.1 jfb 96: root->cr_method = CVS_METHOD_NONE;
1.10 jfb 97: CVS_RSTVR(root);
98:
99: /* enable the most basic commands at least */
100: CVS_SETVR(root, CVS_REQ_VALIDREQ);
101: CVS_SETVR(root, CVS_REQ_VALIDRESP);
1.1 jfb 102:
1.26 joris 103: root->cr_str = xstrdup(str);
104: root->cr_buf = xstrdup(str);
1.1 jfb 105:
106: sp = root->cr_buf;
107: cp = root->cr_buf;
108: if (*sp == ':') {
109: sp++;
1.27 xsa 110: if ((cp = strchr(sp, ':')) == NULL)
111: fatal("failed to parse CVSROOT: unterminated method");
112:
1.1 jfb 113: *(cp++) = '\0';
114:
115: for (i = 0; i < CVS_NBMETHODS; i++) {
116: if (strcmp(sp, cvs_methods[i]) == 0) {
117: root->cr_method = i;
118: break;
119: }
1.14 jfb 120: }
1.27 xsa 121: if (i == CVS_NBMETHODS)
122: fatal("cvsroot_parse: unknown method `%s'", sp);
1.1 jfb 123: }
124:
125: /* find the start of the actual path */
1.27 xsa 126: if ((sp = strchr(cp, '/')) == NULL)
127: fatal("no path specification in CVSROOT");
1.1 jfb 128:
129: root->cr_dir = sp;
130: if (sp == cp) {
131: if (root->cr_method == CVS_METHOD_NONE)
132: root->cr_method = CVS_METHOD_LOCAL;
133: /* stop here, it's just a path */
1.24 joris 134: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
1.1 jfb 135: return (root);
136: }
137:
1.27 xsa 138: if (*(sp - 1) != ':')
139: fatal("missing host/path delimiter in CVSROOT");
140:
1.1 jfb 141: *(sp - 1) = '\0';
142:
143: /*
144: * looks like we have more than just a directory path, so
145: * attempt to split it into user and host parts
146: */
147: sp = strchr(cp, '@');
148: if (sp != NULL) {
149: *(sp++) = '\0';
150:
151: /* password ? */
152: pp = strchr(cp, ':');
153: if (pp != NULL) {
154: *(pp++) = '\0';
155: root->cr_pass = pp;
156: }
157:
158: root->cr_user = cp;
1.12 deraadt 159: } else
1.11 jfb 160: sp = cp;
1.1 jfb 161:
162: pp = strchr(sp, ':');
163: if (pp != NULL) {
164: *(pp++) = '\0';
165: root->cr_port = (u_int)strtol(pp, &cp, 10);
1.27 xsa 166: if ((*cp != '\0') || (root->cr_port > 65535))
167: fatal("invalid port specification in CVSROOT");
1.1 jfb 168:
169: }
170:
171: root->cr_host = sp;
172:
173: if (root->cr_method == CVS_METHOD_NONE) {
174: /* no method found from start of CVSROOT, guess */
175: if (root->cr_host != NULL)
176: root->cr_method = CVS_METHOD_SERVER;
177: else
178: root->cr_method = CVS_METHOD_LOCAL;
179: }
180:
1.6 jfb 181: /* add to the cache */
1.22 joris 182: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
1.1 jfb 183: return (root);
184: }
185:
186: /*
1.25 joris 187: * cvsroot_remove()
1.1 jfb 188: *
1.25 joris 189: * Remove a CVSROOT structure from the cache, and free it.
1.1 jfb 190: */
191: void
1.25 joris 192: cvsroot_remove(struct cvsroot *root)
1.1 jfb 193: {
1.6 jfb 194: root->cr_ref--;
195: if (root->cr_ref == 0) {
1.22 joris 196: TAILQ_REMOVE(&cvs_rcache, root, root_cache);
1.25 joris 197: cvsroot_free(root);
1.6 jfb 198: }
1.1 jfb 199: }
200:
1.25 joris 201: /*
202: * cvsroot_free()
203: *
204: * Free a CVSROOT structure previously allocated and returned by
205: * cvsroot_parse().
206: */
207: static void
208: cvsroot_free(struct cvsroot *root)
209: {
210: if (root->cr_str != NULL)
1.26 joris 211: xfree(root->cr_str);
1.25 joris 212: if (root->cr_buf != NULL)
1.26 joris 213: xfree(root->cr_buf);
1.25 joris 214: if (root->cr_version != NULL)
1.26 joris 215: xfree(root->cr_version);
216: xfree(root);
1.25 joris 217: }
1.1 jfb 218:
219: /*
220: * cvsroot_get()
221: *
222: * Get the CVSROOT information for a specific directory <dir>. The
223: * value is taken from one of 3 possible sources (in order of precedence):
224: *
225: * 1) the `-d' command-line option
226: * 2) the CVS/Root file found in checked-out trees
227: * 3) the CVSROOT environment variable
228: */
1.21 xsa 229: struct cvsroot *
1.1 jfb 230: cvsroot_get(const char *dir)
231: {
232: size_t len;
1.5 jfb 233: char rootpath[MAXPATHLEN], *rootstr, line[128];
1.1 jfb 234: FILE *fp;
235:
236: if (cvs_rootstr != NULL)
237: return cvsroot_parse(cvs_rootstr);
238:
1.29 xsa 239: if (strlcpy(rootpath, dir, sizeof(rootpath)) >= sizeof(rootpath) ||
240: strlcat(rootpath, "/", sizeof(rootpath)) >= sizeof(rootpath) ||
241: strlcat(rootpath, CVS_PATH_ROOTSPEC,
242: sizeof(rootpath)) >= sizeof(rootpath)) {
1.16 xsa 243: errno = ENAMETOOLONG;
1.27 xsa 244: fatal("cvsroot_get: %s: %s", rootpath, strerror(errno));
1.16 xsa 245: }
246:
1.27 xsa 247: if ((fp = fopen(rootpath, "r")) == NULL) {
1.1 jfb 248: if (errno == ENOENT) {
249: /* try env as a last resort */
250: if ((rootstr = getenv("CVSROOT")) != NULL)
251: return cvsroot_parse(rootstr);
252: else
1.27 xsa 253: fatal("cvsroot_get: empty CVSROOT variable");
1.12 deraadt 254: } else {
1.27 xsa 255: fatal("cvsroot_get: fopen: `%s': %s",
256: CVS_PATH_ROOTSPEC, strerror(errno));
1.1 jfb 257: }
258: }
259:
1.27 xsa 260: if (fgets(line, (int)sizeof(line), fp) == NULL)
261: fatal("cvsroot_get: fgets: `%s'", CVS_PATH_ROOTSPEC);
262:
1.5 jfb 263: (void)fclose(fp);
1.1 jfb 264:
1.5 jfb 265: len = strlen(line);
1.13 tedu 266: if (len == 0)
1.17 xsa 267: cvs_log(LP_WARN, "empty %s file", CVS_PATH_ROOTSPEC);
1.13 tedu 268: else if (line[len - 1] == '\n')
1.5 jfb 269: line[--len] = '\0';
1.1 jfb 270:
1.5 jfb 271: return cvsroot_parse(line);
1.1 jfb 272: }