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