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