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