Annotation of src/usr.bin/cvs/root.c, Revision 1.23
1.23 ! xsa 1: /* $OpenBSD: root.c,v 1.22 2005/08/09 10:33:46 joris 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:
27: #include <sys/types.h>
28:
29: #include <err.h>
30: #include <errno.h>
1.19 xsa 31: #include <paths.h>
32: #include <stdio.h>
33: #include <stdlib.h>
1.1 jfb 34: #include <string.h>
1.19 xsa 35: #include <unistd.h>
1.1 jfb 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:
1.20 xsa 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: */
1.22 joris 68: static TAILQ_HEAD(, cvsroot) cvs_rcache = TAILQ_HEAD_INITIALIZER(cvs_rcache);
1.1 jfb 69:
70: /*
71: * cvsroot_parse()
72: *
73: * Parse a CVS root string (as found in CVS/Root files or the CVSROOT
74: * environment variable) and store the fields in a dynamically
75: * allocated cvs_root structure. The format of the string is as follows:
1.6 jfb 76: * :method:[[user[:pass]@host]:path
1.1 jfb 77: * Returns a pointer to the allocated information on success, or NULL
78: * on failure.
79: */
1.21 xsa 80: struct cvsroot *
1.1 jfb 81: cvsroot_parse(const char *str)
82: {
83: u_int i;
84: char *cp, *sp, *pp;
85: struct cvsroot *root;
86:
1.22 joris 87: /*
88: * Look if we have it in cache, if we found it add it to the cache
89: * at the first position again.
90: */
91: TAILQ_FOREACH(root, &cvs_rcache, root_cache) {
92: if (strcmp(str, root->cr_str) == 0) {
93: TAILQ_REMOVE(&cvs_rcache, root, root_cache);
94: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
95: root->cr_ref++;
96: return (root);
1.6 jfb 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.15 jfb 106: root->cr_ref = 1;
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: }
1.14 jfb 144: }
145: if (i == CVS_NBMETHODS) {
146: cvs_log(LP_ERR, "unknown method `%s'", sp);
147: cvsroot_free(root);
148: return (NULL);
1.1 jfb 149: }
150: }
151:
152: /* find the start of the actual path */
153: sp = strchr(cp, '/');
154: if (sp == NULL) {
155: cvs_log(LP_ERR, "no path specification in CVSROOT");
1.6 jfb 156: cvsroot_free(root);
1.1 jfb 157: return (NULL);
158: }
159:
160: root->cr_dir = sp;
161: if (sp == cp) {
162: if (root->cr_method == CVS_METHOD_NONE)
163: root->cr_method = CVS_METHOD_LOCAL;
164: /* stop here, it's just a path */
165: return (root);
166: }
167:
168: if (*(sp - 1) != ':') {
169: cvs_log(LP_ERR, "missing host/path delimiter in CVS root");
1.6 jfb 170: cvsroot_free(root);
1.1 jfb 171: return (NULL);
172: }
173: *(sp - 1) = '\0';
174:
175: /*
176: * looks like we have more than just a directory path, so
177: * attempt to split it into user and host parts
178: */
179: sp = strchr(cp, '@');
180: if (sp != NULL) {
181: *(sp++) = '\0';
182:
183: /* password ? */
184: pp = strchr(cp, ':');
185: if (pp != NULL) {
186: *(pp++) = '\0';
187: root->cr_pass = pp;
188: }
189:
190: root->cr_user = cp;
1.12 deraadt 191: } else
1.11 jfb 192: sp = cp;
1.1 jfb 193:
194: pp = strchr(sp, ':');
195: if (pp != NULL) {
196: *(pp++) = '\0';
197: root->cr_port = (u_int)strtol(pp, &cp, 10);
198: if (*cp != '\0' || root->cr_port > 65535) {
199: cvs_log(LP_ERR,
200: "invalid port specification in CVSROOT");
1.6 jfb 201: cvsroot_free(root);
1.1 jfb 202: return (NULL);
203: }
204:
205: }
206:
207: root->cr_host = sp;
208:
209: if (root->cr_method == CVS_METHOD_NONE) {
210: /* no method found from start of CVSROOT, guess */
211: if (root->cr_host != NULL)
212: root->cr_method = CVS_METHOD_SERVER;
213: else
214: root->cr_method = CVS_METHOD_LOCAL;
215: }
216:
1.6 jfb 217: /* add to the cache */
1.22 joris 218: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
1.1 jfb 219: return (root);
220: }
221:
222:
223: /*
224: * cvsroot_free()
225: *
226: * Free a CVSROOT structure previously allocated and returned by
227: * cvsroot_parse().
228: */
229: void
230: cvsroot_free(struct cvsroot *root)
231: {
1.6 jfb 232: root->cr_ref--;
233: if (root->cr_ref == 0) {
1.22 joris 234: TAILQ_REMOVE(&cvs_rcache, root, root_cache);
1.6 jfb 235: if (root->cr_str != NULL)
236: free(root->cr_str);
237: if (root->cr_buf != NULL)
238: free(root->cr_buf);
1.9 jfb 239: if (root->cr_version != NULL)
240: free(root->cr_version);
1.6 jfb 241: free(root);
242: }
1.1 jfb 243: }
244:
245:
246: /*
247: * cvsroot_get()
248: *
249: * Get the CVSROOT information for a specific directory <dir>. The
250: * value is taken from one of 3 possible sources (in order of precedence):
251: *
252: * 1) the `-d' command-line option
253: * 2) the CVS/Root file found in checked-out trees
254: * 3) the CVSROOT environment variable
255: */
1.21 xsa 256: struct cvsroot *
1.1 jfb 257: cvsroot_get(const char *dir)
258: {
1.16 xsa 259: int l;
1.1 jfb 260: size_t len;
1.5 jfb 261: char rootpath[MAXPATHLEN], *rootstr, line[128];
1.1 jfb 262: FILE *fp;
263:
264: if (cvs_rootstr != NULL)
265: return cvsroot_parse(cvs_rootstr);
266:
1.16 xsa 267: l = snprintf(rootpath, sizeof(rootpath), "%s/" CVS_PATH_ROOTSPEC, dir);
268: if (l == -1 || l >= (int)sizeof(rootpath)) {
269: errno = ENAMETOOLONG;
270: cvs_log(LP_ERRNO, "%s", rootpath);
271: return (NULL);
272: }
273:
1.1 jfb 274: fp = fopen(rootpath, "r");
275: if (fp == NULL) {
276: if (errno == ENOENT) {
277: /* try env as a last resort */
278: if ((rootstr = getenv("CVSROOT")) != NULL)
279: return cvsroot_parse(rootstr);
280: else
281: return (NULL);
1.12 deraadt 282: } else {
1.17 xsa 283: cvs_log(LP_ERRNO, "failed to open %s",
284: CVS_PATH_ROOTSPEC);
1.1 jfb 285: return (NULL);
286: }
287: }
288:
1.23 ! xsa 289: if (fgets(line, (int)sizeof(line), fp) == NULL) {
1.18 jfb 290: cvs_log(LP_ERR, "failed to read line from %s",
1.17 xsa 291: CVS_PATH_ROOTSPEC);
1.1 jfb 292: (void)fclose(fp);
1.5 jfb 293: return (NULL);
1.1 jfb 294: }
1.5 jfb 295: (void)fclose(fp);
1.1 jfb 296:
1.5 jfb 297: len = strlen(line);
1.13 tedu 298: if (len == 0)
1.17 xsa 299: cvs_log(LP_WARN, "empty %s file", CVS_PATH_ROOTSPEC);
1.13 tedu 300: else if (line[len - 1] == '\n')
1.5 jfb 301: line[--len] = '\0';
1.1 jfb 302:
1.5 jfb 303: return cvsroot_parse(line);
1.1 jfb 304: }