Annotation of src/usr.bin/cvs/root.c, Revision 1.24
1.24 ! joris 1: /* $OpenBSD: root.c,v 1.23 2005/08/10 14:49:20 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:
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 */
1.24 ! joris 165: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
1.1 jfb 166: return (root);
167: }
168:
169: if (*(sp - 1) != ':') {
170: cvs_log(LP_ERR, "missing host/path delimiter in CVS root");
1.6 jfb 171: cvsroot_free(root);
1.1 jfb 172: return (NULL);
173: }
174: *(sp - 1) = '\0';
175:
176: /*
177: * looks like we have more than just a directory path, so
178: * attempt to split it into user and host parts
179: */
180: sp = strchr(cp, '@');
181: if (sp != NULL) {
182: *(sp++) = '\0';
183:
184: /* password ? */
185: pp = strchr(cp, ':');
186: if (pp != NULL) {
187: *(pp++) = '\0';
188: root->cr_pass = pp;
189: }
190:
191: root->cr_user = cp;
1.12 deraadt 192: } else
1.11 jfb 193: sp = cp;
1.1 jfb 194:
195: pp = strchr(sp, ':');
196: if (pp != NULL) {
197: *(pp++) = '\0';
198: root->cr_port = (u_int)strtol(pp, &cp, 10);
199: if (*cp != '\0' || root->cr_port > 65535) {
200: cvs_log(LP_ERR,
201: "invalid port specification in CVSROOT");
1.6 jfb 202: cvsroot_free(root);
1.1 jfb 203: return (NULL);
204: }
205:
206: }
207:
208: root->cr_host = sp;
209:
210: if (root->cr_method == CVS_METHOD_NONE) {
211: /* no method found from start of CVSROOT, guess */
212: if (root->cr_host != NULL)
213: root->cr_method = CVS_METHOD_SERVER;
214: else
215: root->cr_method = CVS_METHOD_LOCAL;
216: }
217:
1.6 jfb 218: /* add to the cache */
1.22 joris 219: TAILQ_INSERT_HEAD(&cvs_rcache, root, root_cache);
1.1 jfb 220: return (root);
221: }
222:
223:
224: /*
225: * cvsroot_free()
226: *
227: * Free a CVSROOT structure previously allocated and returned by
228: * cvsroot_parse().
229: */
230: void
231: cvsroot_free(struct cvsroot *root)
232: {
1.6 jfb 233: root->cr_ref--;
234: if (root->cr_ref == 0) {
1.22 joris 235: TAILQ_REMOVE(&cvs_rcache, root, root_cache);
1.6 jfb 236: if (root->cr_str != NULL)
237: free(root->cr_str);
238: if (root->cr_buf != NULL)
239: free(root->cr_buf);
1.9 jfb 240: if (root->cr_version != NULL)
241: free(root->cr_version);
1.6 jfb 242: free(root);
243: }
1.1 jfb 244: }
245:
246:
247: /*
248: * cvsroot_get()
249: *
250: * Get the CVSROOT information for a specific directory <dir>. The
251: * value is taken from one of 3 possible sources (in order of precedence):
252: *
253: * 1) the `-d' command-line option
254: * 2) the CVS/Root file found in checked-out trees
255: * 3) the CVSROOT environment variable
256: */
1.21 xsa 257: struct cvsroot *
1.1 jfb 258: cvsroot_get(const char *dir)
259: {
1.16 xsa 260: int l;
1.1 jfb 261: size_t len;
1.5 jfb 262: char rootpath[MAXPATHLEN], *rootstr, line[128];
1.1 jfb 263: FILE *fp;
264:
265: if (cvs_rootstr != NULL)
266: return cvsroot_parse(cvs_rootstr);
267:
1.16 xsa 268: l = snprintf(rootpath, sizeof(rootpath), "%s/" CVS_PATH_ROOTSPEC, dir);
269: if (l == -1 || l >= (int)sizeof(rootpath)) {
270: errno = ENAMETOOLONG;
271: cvs_log(LP_ERRNO, "%s", rootpath);
272: return (NULL);
273: }
274:
1.1 jfb 275: fp = fopen(rootpath, "r");
276: if (fp == NULL) {
277: if (errno == ENOENT) {
278: /* try env as a last resort */
279: if ((rootstr = getenv("CVSROOT")) != NULL)
280: return cvsroot_parse(rootstr);
281: else
282: return (NULL);
1.12 deraadt 283: } else {
1.17 xsa 284: cvs_log(LP_ERRNO, "failed to open %s",
285: CVS_PATH_ROOTSPEC);
1.1 jfb 286: return (NULL);
287: }
288: }
289:
1.23 xsa 290: if (fgets(line, (int)sizeof(line), fp) == NULL) {
1.18 jfb 291: cvs_log(LP_ERR, "failed to read line from %s",
1.17 xsa 292: CVS_PATH_ROOTSPEC);
1.1 jfb 293: (void)fclose(fp);
1.5 jfb 294: return (NULL);
1.1 jfb 295: }
1.5 jfb 296: (void)fclose(fp);
1.1 jfb 297:
1.5 jfb 298: len = strlen(line);
1.13 tedu 299: if (len == 0)
1.17 xsa 300: cvs_log(LP_WARN, "empty %s file", CVS_PATH_ROOTSPEC);
1.13 tedu 301: else if (line[len - 1] == '\n')
1.5 jfb 302: line[--len] = '\0';
1.1 jfb 303:
1.5 jfb 304: return cvsroot_parse(line);
1.1 jfb 305: }