1*86d7f5d3SJohn Marino /* This program is free software; you can redistribute it and/or modify
2*86d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
3*86d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
4*86d7f5d3SJohn Marino any later version.
5*86d7f5d3SJohn Marino
6*86d7f5d3SJohn Marino This program is distributed in the hope that it will be useful,
7*86d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
8*86d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9*86d7f5d3SJohn Marino GNU General Public License for more details. */
10*86d7f5d3SJohn Marino
11*86d7f5d3SJohn Marino #include "cvs.h"
12*86d7f5d3SJohn Marino
13*86d7f5d3SJohn Marino /* CVS */
14*86d7f5d3SJohn Marino #include "edit.h"
15*86d7f5d3SJohn Marino #include "fileattr.h"
16*86d7f5d3SJohn Marino #include "watch.h"
17*86d7f5d3SJohn Marino
18*86d7f5d3SJohn Marino /* GNULIB */
19*86d7f5d3SJohn Marino #include "buffer.h"
20*86d7f5d3SJohn Marino #include "getline.h"
21*86d7f5d3SJohn Marino #include "getnline.h"
22*86d7f5d3SJohn Marino
23*86d7f5d3SJohn Marino int server_active = 0;
24*86d7f5d3SJohn Marino
25*86d7f5d3SJohn Marino #if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
26*86d7f5d3SJohn Marino
27*86d7f5d3SJohn Marino # include "log-buffer.h"
28*86d7f5d3SJohn Marino # include "ms-buffer.h"
29*86d7f5d3SJohn Marino #endif /* defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) */
30*86d7f5d3SJohn Marino
31*86d7f5d3SJohn Marino #if defined (HAVE_GSSAPI) && defined (SERVER_SUPPORT)
32*86d7f5d3SJohn Marino # include "canon-host.h"
33*86d7f5d3SJohn Marino # include "gssapi-client.h"
34*86d7f5d3SJohn Marino
35*86d7f5d3SJohn Marino /* This stuff isn't included solely with SERVER_SUPPORT since some of these
36*86d7f5d3SJohn Marino * functions (encryption & the like) get compiled with or without server
37*86d7f5d3SJohn Marino * support.
38*86d7f5d3SJohn Marino *
39*86d7f5d3SJohn Marino * FIXME - They should be in a different file.
40*86d7f5d3SJohn Marino */
41*86d7f5d3SJohn Marino /* We use Kerberos 5 routines to map the GSSAPI credential to a user
42*86d7f5d3SJohn Marino name. */
43*86d7f5d3SJohn Marino # include <krb5.h>
44*86d7f5d3SJohn Marino
45*86d7f5d3SJohn Marino static void gserver_authenticate_connection (void);
46*86d7f5d3SJohn Marino
47*86d7f5d3SJohn Marino /* Whether we are already wrapping GSSAPI communication. */
48*86d7f5d3SJohn Marino static int cvs_gssapi_wrapping;
49*86d7f5d3SJohn Marino
50*86d7f5d3SJohn Marino #endif /* defined (HAVE_GSSAPI) && defined (SERVER_SUPPORT) */
51*86d7f5d3SJohn Marino
52*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
53*86d7f5d3SJohn Marino
54*86d7f5d3SJohn Marino extern char *server_hostname;
55*86d7f5d3SJohn Marino
56*86d7f5d3SJohn Marino # if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI)
57*86d7f5d3SJohn Marino # include <sys/socket.h>
58*86d7f5d3SJohn Marino # endif
59*86d7f5d3SJohn Marino
60*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
61*86d7f5d3SJohn Marino # include <syslog.h>
62*86d7f5d3SJohn Marino # ifndef LOG_DAEMON /* for ancient syslogs */
63*86d7f5d3SJohn Marino # define LOG_DAEMON 0
64*86d7f5d3SJohn Marino # endif
65*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
66*86d7f5d3SJohn Marino
67*86d7f5d3SJohn Marino # ifdef HAVE_KERBEROS
68*86d7f5d3SJohn Marino # include <netinet/in.h>
69*86d7f5d3SJohn Marino # include <krb.h>
70*86d7f5d3SJohn Marino # ifndef HAVE_KRB_GET_ERR_TEXT
71*86d7f5d3SJohn Marino # define krb_get_err_text(status) krb_err_txt[status]
72*86d7f5d3SJohn Marino # endif
73*86d7f5d3SJohn Marino
74*86d7f5d3SJohn Marino /* Information we need if we are going to use Kerberos encryption. */
75*86d7f5d3SJohn Marino static C_Block kblock;
76*86d7f5d3SJohn Marino static Key_schedule sched;
77*86d7f5d3SJohn Marino
78*86d7f5d3SJohn Marino # endif /* HAVE_KERBEROS */
79*86d7f5d3SJohn Marino
80*86d7f5d3SJohn Marino /* for select */
81*86d7f5d3SJohn Marino # include "xselect.h"
82*86d7f5d3SJohn Marino
83*86d7f5d3SJohn Marino # ifndef O_NONBLOCK
84*86d7f5d3SJohn Marino # define O_NONBLOCK O_NDELAY
85*86d7f5d3SJohn Marino # endif
86*86d7f5d3SJohn Marino
87*86d7f5d3SJohn Marino /* For initgroups(). */
88*86d7f5d3SJohn Marino # if HAVE_INITGROUPS
89*86d7f5d3SJohn Marino # include <grp.h>
90*86d7f5d3SJohn Marino # endif /* HAVE_INITGROUPS */
91*86d7f5d3SJohn Marino
92*86d7f5d3SJohn Marino # ifdef AUTH_SERVER_SUPPORT
93*86d7f5d3SJohn Marino
94*86d7f5d3SJohn Marino # ifdef HAVE_GETSPNAM
95*86d7f5d3SJohn Marino # include <shadow.h>
96*86d7f5d3SJohn Marino # endif
97*86d7f5d3SJohn Marino
98*86d7f5d3SJohn Marino /* The cvs username sent by the client, which might or might not be
99*86d7f5d3SJohn Marino the same as the system username the server eventually switches to
100*86d7f5d3SJohn Marino run as. CVS_Username gets set iff password authentication is
101*86d7f5d3SJohn Marino successful. */
102*86d7f5d3SJohn Marino char *CVS_Username = NULL;
103*86d7f5d3SJohn Marino
104*86d7f5d3SJohn Marino /* Used to check that same repos is transmitted in pserver auth and in
105*86d7f5d3SJohn Marino later CVS protocol. Exported because root.c also uses. */
106*86d7f5d3SJohn Marino static char *Pserver_Repos = NULL;
107*86d7f5d3SJohn Marino
108*86d7f5d3SJohn Marino # endif /* AUTH_SERVER_SUPPORT */
109*86d7f5d3SJohn Marino
110*86d7f5d3SJohn Marino # ifdef HAVE_PAM
111*86d7f5d3SJohn Marino # if defined(HAVE_SECURITY_PAM_APPL_H)
112*86d7f5d3SJohn Marino # include <security/pam_appl.h>
113*86d7f5d3SJohn Marino # elif defined(HAVE_PAM_PAM_APPL_H)
114*86d7f5d3SJohn Marino # include <pam/pam_appl.h>
115*86d7f5d3SJohn Marino # endif
116*86d7f5d3SJohn Marino
117*86d7f5d3SJohn Marino static pam_handle_t *pamh = NULL;
118*86d7f5d3SJohn Marino
119*86d7f5d3SJohn Marino static char *pam_username;
120*86d7f5d3SJohn Marino static char *pam_password;
121*86d7f5d3SJohn Marino # endif /* HAVE_PAM */
122*86d7f5d3SJohn Marino
123*86d7f5d3SJohn Marino
124*86d7f5d3SJohn Marino
125*86d7f5d3SJohn Marino /* While processing requests, this buffer accumulates data to be sent to
126*86d7f5d3SJohn Marino the client, and then once we are in do_cvs_command, we use it
127*86d7f5d3SJohn Marino for all the data to be sent. */
128*86d7f5d3SJohn Marino static struct buffer *buf_to_net;
129*86d7f5d3SJohn Marino
130*86d7f5d3SJohn Marino /* This buffer is used to read input from the client. */
131*86d7f5d3SJohn Marino static struct buffer *buf_from_net;
132*86d7f5d3SJohn Marino
133*86d7f5d3SJohn Marino
134*86d7f5d3SJohn Marino
135*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
136*86d7f5d3SJohn Marino /* These are the secondary log buffers so that we can disable them after
137*86d7f5d3SJohn Marino * creation, when it is determined that they are unneeded, regardless of what
138*86d7f5d3SJohn Marino * other filters have been prepended to the buffer chain.
139*86d7f5d3SJohn Marino */
140*86d7f5d3SJohn Marino static struct buffer *proxy_log;
141*86d7f5d3SJohn Marino static struct buffer *proxy_log_out;
142*86d7f5d3SJohn Marino
143*86d7f5d3SJohn Marino /* Set while we are reprocessing a log so that we can avoid sending responses
144*86d7f5d3SJohn Marino * to some requests twice.
145*86d7f5d3SJohn Marino */
146*86d7f5d3SJohn Marino static bool reprocessing;
147*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
148*86d7f5d3SJohn Marino
149*86d7f5d3SJohn Marino
150*86d7f5d3SJohn Marino
151*86d7f5d3SJohn Marino /* Arguments storage for `Argument' & `Argumentx' requests. */
152*86d7f5d3SJohn Marino static int argument_count;
153*86d7f5d3SJohn Marino static char **argument_vector;
154*86d7f5d3SJohn Marino static int argument_vector_size;
155*86d7f5d3SJohn Marino
156*86d7f5d3SJohn Marino /*
157*86d7f5d3SJohn Marino * This is where we stash stuff we are going to use. Format string
158*86d7f5d3SJohn Marino * which expects a single directory within it, starting with a slash.
159*86d7f5d3SJohn Marino */
160*86d7f5d3SJohn Marino static char *server_temp_dir;
161*86d7f5d3SJohn Marino
162*86d7f5d3SJohn Marino /* This is the original value of server_temp_dir, before any possible
163*86d7f5d3SJohn Marino changes inserted by serve_max_dotdot. */
164*86d7f5d3SJohn Marino static char *orig_server_temp_dir;
165*86d7f5d3SJohn Marino
166*86d7f5d3SJohn Marino /* Nonzero if we should keep the temp directory around after we exit. */
167*86d7f5d3SJohn Marino static int dont_delete_temp;
168*86d7f5d3SJohn Marino
169*86d7f5d3SJohn Marino static void server_write_entries (void);
170*86d7f5d3SJohn Marino
171*86d7f5d3SJohn Marino cvsroot_t *referrer;
172*86d7f5d3SJohn Marino
173*86d7f5d3SJohn Marino
174*86d7f5d3SJohn Marino
175*86d7f5d3SJohn Marino /* Populate all of the directories between BASE_DIR and its relative
176*86d7f5d3SJohn Marino subdirectory DIR with CVSADM directories. Return 0 for success or
177*86d7f5d3SJohn Marino errno value. */
178*86d7f5d3SJohn Marino static int
create_adm_p(char * base_dir,char * dir)179*86d7f5d3SJohn Marino create_adm_p (char *base_dir, char *dir)
180*86d7f5d3SJohn Marino {
181*86d7f5d3SJohn Marino char *dir_where_cvsadm_lives, *dir_to_register, *p, *tmp;
182*86d7f5d3SJohn Marino int retval, done;
183*86d7f5d3SJohn Marino FILE *f;
184*86d7f5d3SJohn Marino
185*86d7f5d3SJohn Marino if (strcmp (dir, ".") == 0)
186*86d7f5d3SJohn Marino return 0; /* nothing to do */
187*86d7f5d3SJohn Marino
188*86d7f5d3SJohn Marino /* Allocate some space for our directory-munging string. */
189*86d7f5d3SJohn Marino p = xmalloc (strlen (dir) + 1);
190*86d7f5d3SJohn Marino if (p == NULL)
191*86d7f5d3SJohn Marino return ENOMEM;
192*86d7f5d3SJohn Marino
193*86d7f5d3SJohn Marino dir_where_cvsadm_lives = xmalloc (strlen (base_dir) + strlen (dir) + 100);
194*86d7f5d3SJohn Marino if (dir_where_cvsadm_lives == NULL)
195*86d7f5d3SJohn Marino {
196*86d7f5d3SJohn Marino free (p);
197*86d7f5d3SJohn Marino return ENOMEM;
198*86d7f5d3SJohn Marino }
199*86d7f5d3SJohn Marino
200*86d7f5d3SJohn Marino /* Allocate some space for the temporary string in which we will
201*86d7f5d3SJohn Marino construct filenames. */
202*86d7f5d3SJohn Marino tmp = xmalloc (strlen (base_dir) + strlen (dir) + 100);
203*86d7f5d3SJohn Marino if (tmp == NULL)
204*86d7f5d3SJohn Marino {
205*86d7f5d3SJohn Marino free (p);
206*86d7f5d3SJohn Marino free (dir_where_cvsadm_lives);
207*86d7f5d3SJohn Marino return ENOMEM;
208*86d7f5d3SJohn Marino }
209*86d7f5d3SJohn Marino
210*86d7f5d3SJohn Marino
211*86d7f5d3SJohn Marino /* We make several passes through this loop. On the first pass,
212*86d7f5d3SJohn Marino we simply create the CVSADM directory in the deepest directory.
213*86d7f5d3SJohn Marino For each subsequent pass, we try to remove the last path
214*86d7f5d3SJohn Marino element from DIR, create the CVSADM directory in the remaining
215*86d7f5d3SJohn Marino pathname, and register the subdirectory in the newly created
216*86d7f5d3SJohn Marino CVSADM directory. */
217*86d7f5d3SJohn Marino
218*86d7f5d3SJohn Marino retval = done = 0;
219*86d7f5d3SJohn Marino
220*86d7f5d3SJohn Marino strcpy (p, dir);
221*86d7f5d3SJohn Marino strcpy (dir_where_cvsadm_lives, base_dir);
222*86d7f5d3SJohn Marino strcat (dir_where_cvsadm_lives, "/");
223*86d7f5d3SJohn Marino strcat (dir_where_cvsadm_lives, p);
224*86d7f5d3SJohn Marino dir_to_register = NULL;
225*86d7f5d3SJohn Marino
226*86d7f5d3SJohn Marino while (1)
227*86d7f5d3SJohn Marino {
228*86d7f5d3SJohn Marino /* Create CVSADM. */
229*86d7f5d3SJohn Marino (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM);
230*86d7f5d3SJohn Marino if ((CVS_MKDIR (tmp, 0777) < 0) && (errno != EEXIST))
231*86d7f5d3SJohn Marino {
232*86d7f5d3SJohn Marino retval = errno;
233*86d7f5d3SJohn Marino goto finish;
234*86d7f5d3SJohn Marino }
235*86d7f5d3SJohn Marino
236*86d7f5d3SJohn Marino /* Create CVSADM_REP. */
237*86d7f5d3SJohn Marino (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM_REP);
238*86d7f5d3SJohn Marino if (! isfile (tmp))
239*86d7f5d3SJohn Marino {
240*86d7f5d3SJohn Marino /* Use Emptydir as the placeholder until the client sends
241*86d7f5d3SJohn Marino us the real value. This code is similar to checkout.c
242*86d7f5d3SJohn Marino (emptydir_name), but the code below returns errors
243*86d7f5d3SJohn Marino differently. */
244*86d7f5d3SJohn Marino
245*86d7f5d3SJohn Marino char *empty;
246*86d7f5d3SJohn Marino empty = xmalloc (strlen (current_parsed_root->directory)
247*86d7f5d3SJohn Marino + sizeof (CVSROOTADM)
248*86d7f5d3SJohn Marino + sizeof (CVSNULLREPOS)
249*86d7f5d3SJohn Marino + 3);
250*86d7f5d3SJohn Marino if (! empty)
251*86d7f5d3SJohn Marino {
252*86d7f5d3SJohn Marino retval = ENOMEM;
253*86d7f5d3SJohn Marino goto finish;
254*86d7f5d3SJohn Marino }
255*86d7f5d3SJohn Marino
256*86d7f5d3SJohn Marino /* Create the directory name. */
257*86d7f5d3SJohn Marino (void) sprintf (empty, "%s/%s/%s", current_parsed_root->directory,
258*86d7f5d3SJohn Marino CVSROOTADM, CVSNULLREPOS);
259*86d7f5d3SJohn Marino
260*86d7f5d3SJohn Marino /* Create the directory if it doesn't exist. */
261*86d7f5d3SJohn Marino if (! isfile (empty))
262*86d7f5d3SJohn Marino {
263*86d7f5d3SJohn Marino mode_t omask;
264*86d7f5d3SJohn Marino omask = umask (cvsumask);
265*86d7f5d3SJohn Marino if (CVS_MKDIR (empty, 0777) < 0)
266*86d7f5d3SJohn Marino {
267*86d7f5d3SJohn Marino retval = errno;
268*86d7f5d3SJohn Marino free (empty);
269*86d7f5d3SJohn Marino goto finish;
270*86d7f5d3SJohn Marino }
271*86d7f5d3SJohn Marino (void) umask (omask);
272*86d7f5d3SJohn Marino }
273*86d7f5d3SJohn Marino
274*86d7f5d3SJohn Marino f = CVS_FOPEN (tmp, "w");
275*86d7f5d3SJohn Marino if (f == NULL)
276*86d7f5d3SJohn Marino {
277*86d7f5d3SJohn Marino retval = errno;
278*86d7f5d3SJohn Marino free (empty);
279*86d7f5d3SJohn Marino goto finish;
280*86d7f5d3SJohn Marino }
281*86d7f5d3SJohn Marino /* Write the directory name to CVSADM_REP. */
282*86d7f5d3SJohn Marino if (fprintf (f, "%s\n", empty) < 0)
283*86d7f5d3SJohn Marino {
284*86d7f5d3SJohn Marino retval = errno;
285*86d7f5d3SJohn Marino fclose (f);
286*86d7f5d3SJohn Marino free (empty);
287*86d7f5d3SJohn Marino goto finish;
288*86d7f5d3SJohn Marino }
289*86d7f5d3SJohn Marino if (fclose (f) == EOF)
290*86d7f5d3SJohn Marino {
291*86d7f5d3SJohn Marino retval = errno;
292*86d7f5d3SJohn Marino free (empty);
293*86d7f5d3SJohn Marino goto finish;
294*86d7f5d3SJohn Marino }
295*86d7f5d3SJohn Marino
296*86d7f5d3SJohn Marino /* Clean up after ourselves. */
297*86d7f5d3SJohn Marino free (empty);
298*86d7f5d3SJohn Marino }
299*86d7f5d3SJohn Marino
300*86d7f5d3SJohn Marino /* Create CVSADM_ENT. We open in append mode because we
301*86d7f5d3SJohn Marino don't want to clobber an existing Entries file. */
302*86d7f5d3SJohn Marino (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM_ENT);
303*86d7f5d3SJohn Marino f = CVS_FOPEN (tmp, "a");
304*86d7f5d3SJohn Marino if (f == NULL)
305*86d7f5d3SJohn Marino {
306*86d7f5d3SJohn Marino retval = errno;
307*86d7f5d3SJohn Marino goto finish;
308*86d7f5d3SJohn Marino }
309*86d7f5d3SJohn Marino if (fclose (f) == EOF)
310*86d7f5d3SJohn Marino {
311*86d7f5d3SJohn Marino retval = errno;
312*86d7f5d3SJohn Marino goto finish;
313*86d7f5d3SJohn Marino }
314*86d7f5d3SJohn Marino
315*86d7f5d3SJohn Marino if (dir_to_register != NULL)
316*86d7f5d3SJohn Marino {
317*86d7f5d3SJohn Marino /* FIXME: Yes, this results in duplicate entries in the
318*86d7f5d3SJohn Marino Entries.Log file, but it doesn't currently matter. We
319*86d7f5d3SJohn Marino might need to change this later on to make sure that we
320*86d7f5d3SJohn Marino only write one entry. */
321*86d7f5d3SJohn Marino
322*86d7f5d3SJohn Marino Subdir_Register (NULL, dir_where_cvsadm_lives, dir_to_register);
323*86d7f5d3SJohn Marino }
324*86d7f5d3SJohn Marino
325*86d7f5d3SJohn Marino if (done)
326*86d7f5d3SJohn Marino break;
327*86d7f5d3SJohn Marino
328*86d7f5d3SJohn Marino dir_to_register = strrchr (p, '/');
329*86d7f5d3SJohn Marino if (dir_to_register == NULL)
330*86d7f5d3SJohn Marino {
331*86d7f5d3SJohn Marino dir_to_register = p;
332*86d7f5d3SJohn Marino strcpy (dir_where_cvsadm_lives, base_dir);
333*86d7f5d3SJohn Marino done = 1;
334*86d7f5d3SJohn Marino }
335*86d7f5d3SJohn Marino else
336*86d7f5d3SJohn Marino {
337*86d7f5d3SJohn Marino *dir_to_register = '\0';
338*86d7f5d3SJohn Marino dir_to_register++;
339*86d7f5d3SJohn Marino strcpy (dir_where_cvsadm_lives, base_dir);
340*86d7f5d3SJohn Marino strcat (dir_where_cvsadm_lives, "/");
341*86d7f5d3SJohn Marino strcat (dir_where_cvsadm_lives, p);
342*86d7f5d3SJohn Marino }
343*86d7f5d3SJohn Marino }
344*86d7f5d3SJohn Marino
345*86d7f5d3SJohn Marino finish:
346*86d7f5d3SJohn Marino free (tmp);
347*86d7f5d3SJohn Marino free (dir_where_cvsadm_lives);
348*86d7f5d3SJohn Marino free (p);
349*86d7f5d3SJohn Marino return retval;
350*86d7f5d3SJohn Marino }
351*86d7f5d3SJohn Marino
352*86d7f5d3SJohn Marino
353*86d7f5d3SJohn Marino
354*86d7f5d3SJohn Marino /*
355*86d7f5d3SJohn Marino * Make directory DIR, including all intermediate directories if necessary.
356*86d7f5d3SJohn Marino * Returns 0 for success or errno code.
357*86d7f5d3SJohn Marino */
358*86d7f5d3SJohn Marino static int
mkdir_p(char * dir)359*86d7f5d3SJohn Marino mkdir_p (char *dir)
360*86d7f5d3SJohn Marino {
361*86d7f5d3SJohn Marino char *p;
362*86d7f5d3SJohn Marino char *q = xmalloc (strlen (dir) + 1);
363*86d7f5d3SJohn Marino int retval;
364*86d7f5d3SJohn Marino
365*86d7f5d3SJohn Marino if (q == NULL)
366*86d7f5d3SJohn Marino return ENOMEM;
367*86d7f5d3SJohn Marino
368*86d7f5d3SJohn Marino retval = 0;
369*86d7f5d3SJohn Marino
370*86d7f5d3SJohn Marino /*
371*86d7f5d3SJohn Marino * Skip over leading slash if present. We won't bother to try to
372*86d7f5d3SJohn Marino * make '/'.
373*86d7f5d3SJohn Marino */
374*86d7f5d3SJohn Marino p = dir + 1;
375*86d7f5d3SJohn Marino while (1)
376*86d7f5d3SJohn Marino {
377*86d7f5d3SJohn Marino while (*p != '/' && *p != '\0')
378*86d7f5d3SJohn Marino ++p;
379*86d7f5d3SJohn Marino if (*p == '/')
380*86d7f5d3SJohn Marino {
381*86d7f5d3SJohn Marino strncpy (q, dir, p - dir);
382*86d7f5d3SJohn Marino q[p - dir] = '\0';
383*86d7f5d3SJohn Marino if (q[p - dir - 1] != '/' && CVS_MKDIR (q, 0777) < 0)
384*86d7f5d3SJohn Marino {
385*86d7f5d3SJohn Marino int saved_errno = errno;
386*86d7f5d3SJohn Marino
387*86d7f5d3SJohn Marino if (saved_errno != EEXIST
388*86d7f5d3SJohn Marino && ((saved_errno != EACCES && saved_errno != EROFS)
389*86d7f5d3SJohn Marino || !isdir (q)))
390*86d7f5d3SJohn Marino {
391*86d7f5d3SJohn Marino retval = saved_errno;
392*86d7f5d3SJohn Marino goto done;
393*86d7f5d3SJohn Marino }
394*86d7f5d3SJohn Marino }
395*86d7f5d3SJohn Marino ++p;
396*86d7f5d3SJohn Marino }
397*86d7f5d3SJohn Marino else
398*86d7f5d3SJohn Marino {
399*86d7f5d3SJohn Marino if (CVS_MKDIR (dir, 0777) < 0)
400*86d7f5d3SJohn Marino retval = errno;
401*86d7f5d3SJohn Marino goto done;
402*86d7f5d3SJohn Marino }
403*86d7f5d3SJohn Marino }
404*86d7f5d3SJohn Marino done:
405*86d7f5d3SJohn Marino free (q);
406*86d7f5d3SJohn Marino return retval;
407*86d7f5d3SJohn Marino }
408*86d7f5d3SJohn Marino
409*86d7f5d3SJohn Marino
410*86d7f5d3SJohn Marino
411*86d7f5d3SJohn Marino /*
412*86d7f5d3SJohn Marino * Print the error response for error code STATUS. The caller is
413*86d7f5d3SJohn Marino * reponsible for making sure we get back to the command loop without
414*86d7f5d3SJohn Marino * any further output occuring.
415*86d7f5d3SJohn Marino * Must be called only in contexts where it is OK to send output.
416*86d7f5d3SJohn Marino */
417*86d7f5d3SJohn Marino static void
print_error(int status)418*86d7f5d3SJohn Marino print_error (int status)
419*86d7f5d3SJohn Marino {
420*86d7f5d3SJohn Marino char *msg;
421*86d7f5d3SJohn Marino char tmpstr[80];
422*86d7f5d3SJohn Marino
423*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "error ");
424*86d7f5d3SJohn Marino msg = strerror (status);
425*86d7f5d3SJohn Marino if (msg == NULL)
426*86d7f5d3SJohn Marino {
427*86d7f5d3SJohn Marino sprintf (tmpstr, "unknown error %d", status);
428*86d7f5d3SJohn Marino msg = tmpstr;
429*86d7f5d3SJohn Marino }
430*86d7f5d3SJohn Marino buf_output0 (buf_to_net, msg);
431*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
432*86d7f5d3SJohn Marino
433*86d7f5d3SJohn Marino buf_flush (buf_to_net, 0);
434*86d7f5d3SJohn Marino }
435*86d7f5d3SJohn Marino
436*86d7f5d3SJohn Marino
437*86d7f5d3SJohn Marino
438*86d7f5d3SJohn Marino static int pending_error;
439*86d7f5d3SJohn Marino /*
440*86d7f5d3SJohn Marino * Malloc'd text for pending error. Each line must start with "E ". The
441*86d7f5d3SJohn Marino * last line should not end with a newline.
442*86d7f5d3SJohn Marino */
443*86d7f5d3SJohn Marino static char *pending_error_text;
444*86d7f5d3SJohn Marino static char *pending_warning_text;
445*86d7f5d3SJohn Marino
446*86d7f5d3SJohn Marino /* If an error is pending, print it and return 1. If not, return 0.
447*86d7f5d3SJohn Marino Also prints pending warnings, but this does not affect the return value.
448*86d7f5d3SJohn Marino Must be called only in contexts where it is OK to send output. */
449*86d7f5d3SJohn Marino static int
print_pending_error(void)450*86d7f5d3SJohn Marino print_pending_error (void)
451*86d7f5d3SJohn Marino {
452*86d7f5d3SJohn Marino /* Check this case first since it usually means we are out of memory and
453*86d7f5d3SJohn Marino * the buffer output routines might try and allocate memory.
454*86d7f5d3SJohn Marino */
455*86d7f5d3SJohn Marino if (!pending_error_text && pending_error)
456*86d7f5d3SJohn Marino {
457*86d7f5d3SJohn Marino print_error (pending_error);
458*86d7f5d3SJohn Marino pending_error = 0;
459*86d7f5d3SJohn Marino return 1;
460*86d7f5d3SJohn Marino }
461*86d7f5d3SJohn Marino
462*86d7f5d3SJohn Marino if (pending_warning_text)
463*86d7f5d3SJohn Marino {
464*86d7f5d3SJohn Marino buf_output0 (buf_to_net, pending_warning_text);
465*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
466*86d7f5d3SJohn Marino buf_flush (buf_to_net, 0);
467*86d7f5d3SJohn Marino
468*86d7f5d3SJohn Marino free (pending_warning_text);
469*86d7f5d3SJohn Marino pending_warning_text = NULL;
470*86d7f5d3SJohn Marino }
471*86d7f5d3SJohn Marino
472*86d7f5d3SJohn Marino if (pending_error_text)
473*86d7f5d3SJohn Marino {
474*86d7f5d3SJohn Marino buf_output0 (buf_to_net, pending_error_text);
475*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
476*86d7f5d3SJohn Marino if (pending_error)
477*86d7f5d3SJohn Marino print_error (pending_error);
478*86d7f5d3SJohn Marino else
479*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "error \n");
480*86d7f5d3SJohn Marino
481*86d7f5d3SJohn Marino buf_flush (buf_to_net, 0);
482*86d7f5d3SJohn Marino
483*86d7f5d3SJohn Marino pending_error = 0;
484*86d7f5d3SJohn Marino free (pending_error_text);
485*86d7f5d3SJohn Marino pending_error_text = NULL;
486*86d7f5d3SJohn Marino return 1;
487*86d7f5d3SJohn Marino }
488*86d7f5d3SJohn Marino
489*86d7f5d3SJohn Marino return 0;
490*86d7f5d3SJohn Marino }
491*86d7f5d3SJohn Marino
492*86d7f5d3SJohn Marino
493*86d7f5d3SJohn Marino
494*86d7f5d3SJohn Marino /* Is an error pending? */
495*86d7f5d3SJohn Marino # define error_pending() (pending_error || pending_error_text)
496*86d7f5d3SJohn Marino # define warning_pending() (pending_warning_text)
497*86d7f5d3SJohn Marino
498*86d7f5d3SJohn Marino /* Allocate SIZE bytes for pending_error_text and return nonzero
499*86d7f5d3SJohn Marino if we could do it. */
500*86d7f5d3SJohn Marino static inline int
alloc_pending_internal(char ** dest,size_t size)501*86d7f5d3SJohn Marino alloc_pending_internal (char **dest, size_t size)
502*86d7f5d3SJohn Marino {
503*86d7f5d3SJohn Marino *dest = malloc (size);
504*86d7f5d3SJohn Marino if (!*dest)
505*86d7f5d3SJohn Marino {
506*86d7f5d3SJohn Marino pending_error = ENOMEM;
507*86d7f5d3SJohn Marino return 0;
508*86d7f5d3SJohn Marino }
509*86d7f5d3SJohn Marino return 1;
510*86d7f5d3SJohn Marino }
511*86d7f5d3SJohn Marino
512*86d7f5d3SJohn Marino
513*86d7f5d3SJohn Marino
514*86d7f5d3SJohn Marino /* Allocate SIZE bytes for pending_error_text and return nonzero
515*86d7f5d3SJohn Marino if we could do it. */
516*86d7f5d3SJohn Marino static int
alloc_pending(size_t size)517*86d7f5d3SJohn Marino alloc_pending (size_t size)
518*86d7f5d3SJohn Marino {
519*86d7f5d3SJohn Marino if (error_pending ())
520*86d7f5d3SJohn Marino /* Probably alloc_pending callers will have already checked for
521*86d7f5d3SJohn Marino this case. But we might as well handle it if they don't, I
522*86d7f5d3SJohn Marino guess. */
523*86d7f5d3SJohn Marino return 0;
524*86d7f5d3SJohn Marino return alloc_pending_internal (&pending_error_text, size);
525*86d7f5d3SJohn Marino }
526*86d7f5d3SJohn Marino
527*86d7f5d3SJohn Marino
528*86d7f5d3SJohn Marino
529*86d7f5d3SJohn Marino /* Allocate SIZE bytes for pending_error_text and return nonzero
530*86d7f5d3SJohn Marino if we could do it. */
531*86d7f5d3SJohn Marino static int
alloc_pending_warning(size_t size)532*86d7f5d3SJohn Marino alloc_pending_warning (size_t size)
533*86d7f5d3SJohn Marino {
534*86d7f5d3SJohn Marino if (warning_pending ())
535*86d7f5d3SJohn Marino /* Warnings can be lost here. */
536*86d7f5d3SJohn Marino return 0;
537*86d7f5d3SJohn Marino return alloc_pending_internal (&pending_warning_text, size);
538*86d7f5d3SJohn Marino }
539*86d7f5d3SJohn Marino
540*86d7f5d3SJohn Marino
541*86d7f5d3SJohn Marino
542*86d7f5d3SJohn Marino static int
supported_response(char * name)543*86d7f5d3SJohn Marino supported_response (char *name)
544*86d7f5d3SJohn Marino {
545*86d7f5d3SJohn Marino struct response *rs;
546*86d7f5d3SJohn Marino
547*86d7f5d3SJohn Marino for (rs = responses; rs->name != NULL; ++rs)
548*86d7f5d3SJohn Marino if (strcmp (rs->name, name) == 0)
549*86d7f5d3SJohn Marino return rs->status == rs_supported;
550*86d7f5d3SJohn Marino error (1, 0, "internal error: testing support for unknown response?");
551*86d7f5d3SJohn Marino /* NOTREACHED */
552*86d7f5d3SJohn Marino return 0;
553*86d7f5d3SJohn Marino }
554*86d7f5d3SJohn Marino
555*86d7f5d3SJohn Marino
556*86d7f5d3SJohn Marino
557*86d7f5d3SJohn Marino /*
558*86d7f5d3SJohn Marino * Return true if we need to relay write requests to a primary server
559*86d7f5d3SJohn Marino * and false otherwise.
560*86d7f5d3SJohn Marino *
561*86d7f5d3SJohn Marino * NOTES
562*86d7f5d3SJohn Marino *
563*86d7f5d3SJohn Marino * - primarily handles :ext: method as this seems most likely to be used in
564*86d7f5d3SJohn Marino * practice.
565*86d7f5d3SJohn Marino *
566*86d7f5d3SJohn Marino * - :fork: method is handled for testing.
567*86d7f5d3SJohn Marino *
568*86d7f5d3SJohn Marino * - Could handle pserver too, but would have to store the password
569*86d7f5d3SJohn Marino * the client sent us.
570*86d7f5d3SJohn Marino *
571*86d7f5d3SJohn Marino *
572*86d7f5d3SJohn Marino * GLOBALS
573*86d7f5d3SJohn Marino * config->PrimaryServer
574*86d7f5d3SJohn Marino * The parsed setting from CVSROOT/config, if any, or
575*86d7f5d3SJohn Marino * NULL, otherwise.
576*86d7f5d3SJohn Marino * current_parsed_root The current repository.
577*86d7f5d3SJohn Marino *
578*86d7f5d3SJohn Marino * RETURNS
579*86d7f5d3SJohn Marino * true If this server is configured as a secondary server.
580*86d7f5d3SJohn Marino * false Otherwise.
581*86d7f5d3SJohn Marino */
582*86d7f5d3SJohn Marino static inline bool
isProxyServer(void)583*86d7f5d3SJohn Marino isProxyServer (void)
584*86d7f5d3SJohn Marino {
585*86d7f5d3SJohn Marino assert (current_parsed_root);
586*86d7f5d3SJohn Marino
587*86d7f5d3SJohn Marino /***
588*86d7f5d3SJohn Marino *** The following is done as a series of if/return combinations an an
589*86d7f5d3SJohn Marino *** optimization.
590*86d7f5d3SJohn Marino ***/
591*86d7f5d3SJohn Marino
592*86d7f5d3SJohn Marino /* If there is no primary server defined in CVSROOT/config, then we can't
593*86d7f5d3SJohn Marino * be a secondary.
594*86d7f5d3SJohn Marino */
595*86d7f5d3SJohn Marino if (!config || !config->PrimaryServer) return false;
596*86d7f5d3SJohn Marino
597*86d7f5d3SJohn Marino /* The directory must not match for all methods. */
598*86d7f5d3SJohn Marino if (!isSamePath (config->PrimaryServer->directory,
599*86d7f5d3SJohn Marino current_parsed_root->directory))
600*86d7f5d3SJohn Marino return true;
601*86d7f5d3SJohn Marino
602*86d7f5d3SJohn Marino /* Only the directory is important for fork. */
603*86d7f5d3SJohn Marino if (config->PrimaryServer->method == fork_method)
604*86d7f5d3SJohn Marino return false;
605*86d7f5d3SJohn Marino
606*86d7f5d3SJohn Marino /* Must be :ext: method, then. This is enforced when CVSROOT/config is
607*86d7f5d3SJohn Marino * parsed.
608*86d7f5d3SJohn Marino */
609*86d7f5d3SJohn Marino assert (config->PrimaryServer->isremote);
610*86d7f5d3SJohn Marino
611*86d7f5d3SJohn Marino if (isThisHost (config->PrimaryServer->hostname))
612*86d7f5d3SJohn Marino return false;
613*86d7f5d3SJohn Marino
614*86d7f5d3SJohn Marino return true;
615*86d7f5d3SJohn Marino }
616*86d7f5d3SJohn Marino
617*86d7f5d3SJohn Marino
618*86d7f5d3SJohn Marino
619*86d7f5d3SJohn Marino static void
serve_valid_responses(char * arg)620*86d7f5d3SJohn Marino serve_valid_responses (char *arg)
621*86d7f5d3SJohn Marino {
622*86d7f5d3SJohn Marino char *p = arg;
623*86d7f5d3SJohn Marino char *q;
624*86d7f5d3SJohn Marino struct response *rs;
625*86d7f5d3SJohn Marino
626*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
627*86d7f5d3SJohn Marino /* Process this in the first pass since the data it gathers can be used
628*86d7f5d3SJohn Marino * prior to a `Root' request.
629*86d7f5d3SJohn Marino */
630*86d7f5d3SJohn Marino if (reprocessing) return;
631*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
632*86d7f5d3SJohn Marino
633*86d7f5d3SJohn Marino do
634*86d7f5d3SJohn Marino {
635*86d7f5d3SJohn Marino q = strchr (p, ' ');
636*86d7f5d3SJohn Marino if (q != NULL)
637*86d7f5d3SJohn Marino *q++ = '\0';
638*86d7f5d3SJohn Marino for (rs = responses; rs->name != NULL; ++rs)
639*86d7f5d3SJohn Marino {
640*86d7f5d3SJohn Marino if (strcmp (rs->name, p) == 0)
641*86d7f5d3SJohn Marino break;
642*86d7f5d3SJohn Marino }
643*86d7f5d3SJohn Marino if (rs->name == NULL)
644*86d7f5d3SJohn Marino /*
645*86d7f5d3SJohn Marino * It is a response we have never heard of (and thus never
646*86d7f5d3SJohn Marino * will want to use). So don't worry about it.
647*86d7f5d3SJohn Marino */
648*86d7f5d3SJohn Marino ;
649*86d7f5d3SJohn Marino else
650*86d7f5d3SJohn Marino rs->status = rs_supported;
651*86d7f5d3SJohn Marino p = q;
652*86d7f5d3SJohn Marino } while (q != NULL);
653*86d7f5d3SJohn Marino for (rs = responses; rs->name != NULL; ++rs)
654*86d7f5d3SJohn Marino {
655*86d7f5d3SJohn Marino if (rs->status == rs_essential)
656*86d7f5d3SJohn Marino {
657*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E response `");
658*86d7f5d3SJohn Marino buf_output0 (buf_to_net, rs->name);
659*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "' not supported by client\nerror \n");
660*86d7f5d3SJohn Marino
661*86d7f5d3SJohn Marino /* FIXME: This call to buf_flush could conceivably
662*86d7f5d3SJohn Marino cause deadlock, as noted in server_cleanup. */
663*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
664*86d7f5d3SJohn Marino
665*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
666*86d7f5d3SJohn Marino }
667*86d7f5d3SJohn Marino else if (rs->status == rs_optional)
668*86d7f5d3SJohn Marino rs->status = rs_not_supported;
669*86d7f5d3SJohn Marino }
670*86d7f5d3SJohn Marino }
671*86d7f5d3SJohn Marino
672*86d7f5d3SJohn Marino
673*86d7f5d3SJohn Marino
674*86d7f5d3SJohn Marino /*
675*86d7f5d3SJohn Marino * Process IDs of the subprocess, or negative if that subprocess
676*86d7f5d3SJohn Marino * does not exist.
677*86d7f5d3SJohn Marino */
678*86d7f5d3SJohn Marino static pid_t command_pid;
679*86d7f5d3SJohn Marino
680*86d7f5d3SJohn Marino static void
outbuf_memory_error(struct buffer * buf)681*86d7f5d3SJohn Marino outbuf_memory_error (struct buffer *buf)
682*86d7f5d3SJohn Marino {
683*86d7f5d3SJohn Marino static const char msg[] = "E Fatal server error\n\
684*86d7f5d3SJohn Marino error ENOMEM Virtual memory exhausted.\n";
685*86d7f5d3SJohn Marino if (command_pid > 0)
686*86d7f5d3SJohn Marino kill (command_pid, SIGTERM);
687*86d7f5d3SJohn Marino
688*86d7f5d3SJohn Marino /*
689*86d7f5d3SJohn Marino * We have arranged things so that printing this now either will
690*86d7f5d3SJohn Marino * be valid, or the "E fatal error" line will get glommed onto the
691*86d7f5d3SJohn Marino * end of an existing "E" or "M" response.
692*86d7f5d3SJohn Marino */
693*86d7f5d3SJohn Marino
694*86d7f5d3SJohn Marino /* If this gives an error, not much we could do. syslog() it? */
695*86d7f5d3SJohn Marino write (STDOUT_FILENO, msg, sizeof (msg) - 1);
696*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
697*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR, "virtual memory exhausted");
698*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
699*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
700*86d7f5d3SJohn Marino }
701*86d7f5d3SJohn Marino
702*86d7f5d3SJohn Marino
703*86d7f5d3SJohn Marino
704*86d7f5d3SJohn Marino static void
input_memory_error(struct buffer * buf)705*86d7f5d3SJohn Marino input_memory_error (struct buffer *buf)
706*86d7f5d3SJohn Marino {
707*86d7f5d3SJohn Marino outbuf_memory_error (buf);
708*86d7f5d3SJohn Marino }
709*86d7f5d3SJohn Marino
710*86d7f5d3SJohn Marino
711*86d7f5d3SJohn Marino
712*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
713*86d7f5d3SJohn Marino /* This function rewinds the net connection using the write proxy log file.
714*86d7f5d3SJohn Marino *
715*86d7f5d3SJohn Marino * GLOBALS
716*86d7f5d3SJohn Marino * proxy_log The buffer object containing the write proxy log.
717*86d7f5d3SJohn Marino *
718*86d7f5d3SJohn Marino * RETURNS
719*86d7f5d3SJohn Marino * Nothing.
720*86d7f5d3SJohn Marino */
721*86d7f5d3SJohn Marino static void
rewind_buf_from_net(void)722*86d7f5d3SJohn Marino rewind_buf_from_net (void)
723*86d7f5d3SJohn Marino {
724*86d7f5d3SJohn Marino struct buffer *log;
725*86d7f5d3SJohn Marino
726*86d7f5d3SJohn Marino assert (proxy_log);
727*86d7f5d3SJohn Marino
728*86d7f5d3SJohn Marino /* Free the arguments since we processed some of them in the first pass.
729*86d7f5d3SJohn Marino */
730*86d7f5d3SJohn Marino {
731*86d7f5d3SJohn Marino /* argument_vector[0] is a dummy argument, we don't mess with
732*86d7f5d3SJohn Marino * it.
733*86d7f5d3SJohn Marino */
734*86d7f5d3SJohn Marino char **cp;
735*86d7f5d3SJohn Marino for (cp = argument_vector + 1;
736*86d7f5d3SJohn Marino cp < argument_vector + argument_count;
737*86d7f5d3SJohn Marino ++cp)
738*86d7f5d3SJohn Marino free (*cp);
739*86d7f5d3SJohn Marino
740*86d7f5d3SJohn Marino argument_count = 1;
741*86d7f5d3SJohn Marino }
742*86d7f5d3SJohn Marino
743*86d7f5d3SJohn Marino log = log_buffer_rewind (proxy_log);
744*86d7f5d3SJohn Marino proxy_log = NULL;
745*86d7f5d3SJohn Marino /* Dispose of any read but unused data in the net buffer since it will
746*86d7f5d3SJohn Marino * already be in the log.
747*86d7f5d3SJohn Marino */
748*86d7f5d3SJohn Marino buf_free_data (buf_from_net);
749*86d7f5d3SJohn Marino buf_from_net = ms_buffer_initialize (outbuf_memory_error, log,
750*86d7f5d3SJohn Marino buf_from_net);
751*86d7f5d3SJohn Marino reprocessing = true;
752*86d7f5d3SJohn Marino }
753*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
754*86d7f5d3SJohn Marino
755*86d7f5d3SJohn Marino
756*86d7f5d3SJohn Marino
757*86d7f5d3SJohn Marino char *gConfigPath;
758*86d7f5d3SJohn Marino
759*86d7f5d3SJohn Marino
760*86d7f5d3SJohn Marino
761*86d7f5d3SJohn Marino /*
762*86d7f5d3SJohn Marino * This request cannot be ignored by a potential secondary since it is used to
763*86d7f5d3SJohn Marino * determine if we _are_ a secondary.
764*86d7f5d3SJohn Marino */
765*86d7f5d3SJohn Marino static void
serve_root(char * arg)766*86d7f5d3SJohn Marino serve_root (char *arg)
767*86d7f5d3SJohn Marino {
768*86d7f5d3SJohn Marino char *path;
769*86d7f5d3SJohn Marino
770*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "serve_root (%s)", arg ? arg : "(null)");
771*86d7f5d3SJohn Marino
772*86d7f5d3SJohn Marino /* Don't process this twice or when errors are pending. */
773*86d7f5d3SJohn Marino if (error_pending()
774*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
775*86d7f5d3SJohn Marino || reprocessing
776*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
777*86d7f5d3SJohn Marino ) return;
778*86d7f5d3SJohn Marino
779*86d7f5d3SJohn Marino if (!ISABSOLUTE (arg))
780*86d7f5d3SJohn Marino {
781*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (arg)))
782*86d7f5d3SJohn Marino sprintf (pending_error_text,
783*86d7f5d3SJohn Marino "E Root %s must be an absolute pathname", arg);
784*86d7f5d3SJohn Marino return;
785*86d7f5d3SJohn Marino }
786*86d7f5d3SJohn Marino
787*86d7f5d3SJohn Marino /* Sending "Root" twice is invalid.
788*86d7f5d3SJohn Marino
789*86d7f5d3SJohn Marino The other way to handle a duplicate Root requests would be as a
790*86d7f5d3SJohn Marino request to clear out all state and start over as if it was a
791*86d7f5d3SJohn Marino new connection. Doing this would cause interoperability
792*86d7f5d3SJohn Marino headaches, so it should be a different request, if there is
793*86d7f5d3SJohn Marino any reason why such a feature is needed. */
794*86d7f5d3SJohn Marino if (current_parsed_root != NULL)
795*86d7f5d3SJohn Marino {
796*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (arg)))
797*86d7f5d3SJohn Marino sprintf (pending_error_text,
798*86d7f5d3SJohn Marino "E Protocol error: Duplicate Root request, for %s", arg);
799*86d7f5d3SJohn Marino return;
800*86d7f5d3SJohn Marino }
801*86d7f5d3SJohn Marino
802*86d7f5d3SJohn Marino /* Set original_parsed_root here, not because it can be changed in the
803*86d7f5d3SJohn Marino * client Redirect sense, but so we don't have to switch in code that
804*86d7f5d3SJohn Marino * runs in both modes to decide which to print.
805*86d7f5d3SJohn Marino */
806*86d7f5d3SJohn Marino original_parsed_root = current_parsed_root = local_cvsroot (arg);
807*86d7f5d3SJohn Marino
808*86d7f5d3SJohn Marino # ifdef AUTH_SERVER_SUPPORT
809*86d7f5d3SJohn Marino if (Pserver_Repos != NULL)
810*86d7f5d3SJohn Marino {
811*86d7f5d3SJohn Marino if (strcmp (Pserver_Repos, current_parsed_root->directory) != 0)
812*86d7f5d3SJohn Marino {
813*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (Pserver_Repos)
814*86d7f5d3SJohn Marino + strlen (current_parsed_root->directory)))
815*86d7f5d3SJohn Marino /* The explicitness is to aid people who are writing clients.
816*86d7f5d3SJohn Marino I don't see how this information could help an
817*86d7f5d3SJohn Marino attacker. */
818*86d7f5d3SJohn Marino sprintf (pending_error_text, "\
819*86d7f5d3SJohn Marino E Protocol error: Root says \"%s\" but pserver says \"%s\"",
820*86d7f5d3SJohn Marino current_parsed_root->directory, Pserver_Repos);
821*86d7f5d3SJohn Marino return;
822*86d7f5d3SJohn Marino }
823*86d7f5d3SJohn Marino }
824*86d7f5d3SJohn Marino # endif
825*86d7f5d3SJohn Marino
826*86d7f5d3SJohn Marino /* For pserver, this will already have happened, and the call will do
827*86d7f5d3SJohn Marino nothing. But for rsh, we need to do it now. */
828*86d7f5d3SJohn Marino config = get_root_allow_config (current_parsed_root->directory,
829*86d7f5d3SJohn Marino gConfigPath);
830*86d7f5d3SJohn Marino
831*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
832*86d7f5d3SJohn Marino /* At this point we have enough information to determine if we are a
833*86d7f5d3SJohn Marino * secondary server or not.
834*86d7f5d3SJohn Marino */
835*86d7f5d3SJohn Marino if (proxy_log && !isProxyServer ())
836*86d7f5d3SJohn Marino {
837*86d7f5d3SJohn Marino /* Else we are not a secondary server. There is no point in
838*86d7f5d3SJohn Marino * reprocessing since we handle all the requests we can receive
839*86d7f5d3SJohn Marino * before `Root' as we receive them. But close the logs.
840*86d7f5d3SJohn Marino */
841*86d7f5d3SJohn Marino log_buffer_closelog (proxy_log);
842*86d7f5d3SJohn Marino log_buffer_closelog (proxy_log_out);
843*86d7f5d3SJohn Marino proxy_log = NULL;
844*86d7f5d3SJohn Marino /*
845*86d7f5d3SJohn Marino * Don't need this. We assume it when proxy_log == NULL.
846*86d7f5d3SJohn Marino *
847*86d7f5d3SJohn Marino * proxy_log_out = NULL;
848*86d7f5d3SJohn Marino */
849*86d7f5d3SJohn Marino }
850*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
851*86d7f5d3SJohn Marino
852*86d7f5d3SJohn Marino /* Now set the TMPDIR environment variable. If it was set in the config
853*86d7f5d3SJohn Marino * file, we now know it.
854*86d7f5d3SJohn Marino */
855*86d7f5d3SJohn Marino push_env_temp_dir ();
856*86d7f5d3SJohn Marino
857*86d7f5d3SJohn Marino /* OK, now figure out where we stash our temporary files. */
858*86d7f5d3SJohn Marino {
859*86d7f5d3SJohn Marino char *p;
860*86d7f5d3SJohn Marino
861*86d7f5d3SJohn Marino /* The code which wants to chdir into server_temp_dir is not set
862*86d7f5d3SJohn Marino * up to deal with it being a relative path. So give an error
863*86d7f5d3SJohn Marino * for that case.
864*86d7f5d3SJohn Marino */
865*86d7f5d3SJohn Marino if (!ISABSOLUTE (get_cvs_tmp_dir ()))
866*86d7f5d3SJohn Marino {
867*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (get_cvs_tmp_dir ())))
868*86d7f5d3SJohn Marino sprintf (pending_error_text,
869*86d7f5d3SJohn Marino "E Value of %s for TMPDIR is not absolute",
870*86d7f5d3SJohn Marino get_cvs_tmp_dir ());
871*86d7f5d3SJohn Marino
872*86d7f5d3SJohn Marino /* FIXME: we would like this error to be persistent, that
873*86d7f5d3SJohn Marino * is, not cleared by print_pending_error. The current client
874*86d7f5d3SJohn Marino * will exit as soon as it gets an error, but the protocol spec
875*86d7f5d3SJohn Marino * does not require a client to do so.
876*86d7f5d3SJohn Marino */
877*86d7f5d3SJohn Marino }
878*86d7f5d3SJohn Marino else
879*86d7f5d3SJohn Marino {
880*86d7f5d3SJohn Marino int status;
881*86d7f5d3SJohn Marino int i = 0;
882*86d7f5d3SJohn Marino
883*86d7f5d3SJohn Marino server_temp_dir = xmalloc (strlen (get_cvs_tmp_dir ()) + 80);
884*86d7f5d3SJohn Marino if (!server_temp_dir)
885*86d7f5d3SJohn Marino {
886*86d7f5d3SJohn Marino /* Strictly speaking, we're not supposed to output anything
887*86d7f5d3SJohn Marino * now. But we're about to exit(), give it a try.
888*86d7f5d3SJohn Marino */
889*86d7f5d3SJohn Marino printf ("E Fatal server error, aborting.\n\
890*86d7f5d3SJohn Marino error ENOMEM Virtual memory exhausted.\n");
891*86d7f5d3SJohn Marino
892*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
893*86d7f5d3SJohn Marino }
894*86d7f5d3SJohn Marino strcpy (server_temp_dir, get_cvs_tmp_dir ());
895*86d7f5d3SJohn Marino
896*86d7f5d3SJohn Marino /* Remove a trailing slash from TMPDIR if present. */
897*86d7f5d3SJohn Marino p = server_temp_dir + strlen (server_temp_dir) - 1;
898*86d7f5d3SJohn Marino if (*p == '/')
899*86d7f5d3SJohn Marino *p = '\0';
900*86d7f5d3SJohn Marino
901*86d7f5d3SJohn Marino /* I wanted to use cvs-serv/PID, but then you have to worry about
902*86d7f5d3SJohn Marino * the permissions on the cvs-serv directory being right. So
903*86d7f5d3SJohn Marino * use cvs-servPID.
904*86d7f5d3SJohn Marino */
905*86d7f5d3SJohn Marino strcat (server_temp_dir, "/cvs-serv");
906*86d7f5d3SJohn Marino
907*86d7f5d3SJohn Marino p = server_temp_dir + strlen (server_temp_dir);
908*86d7f5d3SJohn Marino sprintf (p, "%ld", (long) getpid ());
909*86d7f5d3SJohn Marino
910*86d7f5d3SJohn Marino orig_server_temp_dir = server_temp_dir;
911*86d7f5d3SJohn Marino
912*86d7f5d3SJohn Marino /* Create the temporary directory, and set the mode to
913*86d7f5d3SJohn Marino * 700, to discourage random people from tampering with
914*86d7f5d3SJohn Marino * it.
915*86d7f5d3SJohn Marino */
916*86d7f5d3SJohn Marino while ((status = mkdir_p (server_temp_dir)) == EEXIST)
917*86d7f5d3SJohn Marino {
918*86d7f5d3SJohn Marino static const char suffix[] = "abcdefghijklmnopqrstuvwxyz";
919*86d7f5d3SJohn Marino
920*86d7f5d3SJohn Marino if (i >= sizeof suffix - 1) break;
921*86d7f5d3SJohn Marino if (i == 0) p = server_temp_dir + strlen (server_temp_dir);
922*86d7f5d3SJohn Marino p[0] = suffix[i++];
923*86d7f5d3SJohn Marino p[1] = '\0';
924*86d7f5d3SJohn Marino }
925*86d7f5d3SJohn Marino if (status)
926*86d7f5d3SJohn Marino {
927*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (server_temp_dir)))
928*86d7f5d3SJohn Marino sprintf (pending_error_text,
929*86d7f5d3SJohn Marino "E can't create temporary directory %s",
930*86d7f5d3SJohn Marino server_temp_dir);
931*86d7f5d3SJohn Marino pending_error = status;
932*86d7f5d3SJohn Marino }
933*86d7f5d3SJohn Marino #ifndef CHMOD_BROKEN
934*86d7f5d3SJohn Marino else if (chmod (server_temp_dir, S_IRWXU) < 0)
935*86d7f5d3SJohn Marino {
936*86d7f5d3SJohn Marino int save_errno = errno;
937*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (server_temp_dir)))
938*86d7f5d3SJohn Marino sprintf (pending_error_text,
939*86d7f5d3SJohn Marino "E cannot change permissions on temporary directory %s",
940*86d7f5d3SJohn Marino server_temp_dir);
941*86d7f5d3SJohn Marino pending_error = save_errno;
942*86d7f5d3SJohn Marino }
943*86d7f5d3SJohn Marino #endif
944*86d7f5d3SJohn Marino else if (CVS_CHDIR (server_temp_dir) < 0)
945*86d7f5d3SJohn Marino {
946*86d7f5d3SJohn Marino int save_errno = errno;
947*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (server_temp_dir)))
948*86d7f5d3SJohn Marino sprintf (pending_error_text,
949*86d7f5d3SJohn Marino "E cannot change to temporary directory %s",
950*86d7f5d3SJohn Marino server_temp_dir);
951*86d7f5d3SJohn Marino pending_error = save_errno;
952*86d7f5d3SJohn Marino }
953*86d7f5d3SJohn Marino }
954*86d7f5d3SJohn Marino }
955*86d7f5d3SJohn Marino
956*86d7f5d3SJohn Marino /* Now that we have a config, verify our compression level. Since
957*86d7f5d3SJohn Marino * most clients do not send Gzip-stream requests until after the root
958*86d7f5d3SJohn Marino * request, wait until the first request following Root to verify that
959*86d7f5d3SJohn Marino * compression is being used when level 0 is not allowed.
960*86d7f5d3SJohn Marino */
961*86d7f5d3SJohn Marino if (gzip_level)
962*86d7f5d3SJohn Marino {
963*86d7f5d3SJohn Marino bool forced = false;
964*86d7f5d3SJohn Marino
965*86d7f5d3SJohn Marino if (gzip_level < config->MinCompressionLevel)
966*86d7f5d3SJohn Marino {
967*86d7f5d3SJohn Marino gzip_level = config->MinCompressionLevel;
968*86d7f5d3SJohn Marino forced = true;
969*86d7f5d3SJohn Marino }
970*86d7f5d3SJohn Marino
971*86d7f5d3SJohn Marino if (gzip_level > config->MaxCompressionLevel)
972*86d7f5d3SJohn Marino {
973*86d7f5d3SJohn Marino gzip_level = config->MaxCompressionLevel;
974*86d7f5d3SJohn Marino forced = true;
975*86d7f5d3SJohn Marino }
976*86d7f5d3SJohn Marino
977*86d7f5d3SJohn Marino if (forced && !quiet
978*86d7f5d3SJohn Marino && alloc_pending_warning (120 + strlen (program_name)))
979*86d7f5d3SJohn Marino sprintf (pending_warning_text,
980*86d7f5d3SJohn Marino "E %s server: Forcing compression level %d (allowed: %d <= z <= %d).",
981*86d7f5d3SJohn Marino program_name, gzip_level, config->MinCompressionLevel,
982*86d7f5d3SJohn Marino config->MaxCompressionLevel);
983*86d7f5d3SJohn Marino }
984*86d7f5d3SJohn Marino
985*86d7f5d3SJohn Marino path = xmalloc (strlen (current_parsed_root->directory)
986*86d7f5d3SJohn Marino + sizeof (CVSROOTADM)
987*86d7f5d3SJohn Marino + 2);
988*86d7f5d3SJohn Marino if (path == NULL)
989*86d7f5d3SJohn Marino {
990*86d7f5d3SJohn Marino pending_error = ENOMEM;
991*86d7f5d3SJohn Marino return;
992*86d7f5d3SJohn Marino }
993*86d7f5d3SJohn Marino (void) sprintf (path, "%s/%s", current_parsed_root->directory, CVSROOTADM);
994*86d7f5d3SJohn Marino if (!isaccessible (path, R_OK | X_OK))
995*86d7f5d3SJohn Marino {
996*86d7f5d3SJohn Marino int save_errno = errno;
997*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (path)))
998*86d7f5d3SJohn Marino sprintf (pending_error_text, "E Cannot access %s", path);
999*86d7f5d3SJohn Marino pending_error = save_errno;
1000*86d7f5d3SJohn Marino }
1001*86d7f5d3SJohn Marino free (path);
1002*86d7f5d3SJohn Marino
1003*86d7f5d3SJohn Marino setenv (CVSROOT_ENV, current_parsed_root->directory, 1);
1004*86d7f5d3SJohn Marino }
1005*86d7f5d3SJohn Marino
1006*86d7f5d3SJohn Marino
1007*86d7f5d3SJohn Marino
1008*86d7f5d3SJohn Marino static int max_dotdot_limit = 0;
1009*86d7f5d3SJohn Marino
1010*86d7f5d3SJohn Marino /* Is this pathname OK to recurse into when we are running as the server?
1011*86d7f5d3SJohn Marino If not, call error() with a fatal error. */
1012*86d7f5d3SJohn Marino void
server_pathname_check(char * path)1013*86d7f5d3SJohn Marino server_pathname_check (char *path)
1014*86d7f5d3SJohn Marino {
1015*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "server_pathname_check (%s)",
1016*86d7f5d3SJohn Marino path ? path : "(null)");
1017*86d7f5d3SJohn Marino
1018*86d7f5d3SJohn Marino /* An absolute pathname is almost surely a path on the *client* machine,
1019*86d7f5d3SJohn Marino and is unlikely to do us any good here. It also is probably capable
1020*86d7f5d3SJohn Marino of being a security hole in the anonymous readonly case. */
1021*86d7f5d3SJohn Marino if (ISABSOLUTE (path))
1022*86d7f5d3SJohn Marino /* Giving an error is actually kind of a cop-out, in the sense
1023*86d7f5d3SJohn Marino that it would be nice for "cvs co -d /foo/bar/baz" to work.
1024*86d7f5d3SJohn Marino A quick fix in the server would be requiring Max-dotdot of
1025*86d7f5d3SJohn Marino at least one if pathnames are absolute, and then putting
1026*86d7f5d3SJohn Marino /abs/foo/bar/baz in the temp dir beside the /d/d/d stuff.
1027*86d7f5d3SJohn Marino A cleaner fix in the server might be to decouple the
1028*86d7f5d3SJohn Marino pathnames we pass back to the client from pathnames in our
1029*86d7f5d3SJohn Marino temp directory (this would also probably remove the need
1030*86d7f5d3SJohn Marino for Max-dotdot). A fix in the client would have the client
1031*86d7f5d3SJohn Marino turn it into "cd /foo/bar; cvs co -d baz" (more or less).
1032*86d7f5d3SJohn Marino This probably has some problems with pathnames which appear
1033*86d7f5d3SJohn Marino in messages. */
1034*86d7f5d3SJohn Marino error ( 1, 0,
1035*86d7f5d3SJohn Marino "absolute pathnames invalid for server (specified `%s')",
1036*86d7f5d3SJohn Marino path );
1037*86d7f5d3SJohn Marino if (pathname_levels (path) > max_dotdot_limit)
1038*86d7f5d3SJohn Marino {
1039*86d7f5d3SJohn Marino /* Similar to the ISABSOLUTE case in security implications. */
1040*86d7f5d3SJohn Marino error (0, 0, "protocol error: `%s' contains more leading ..", path);
1041*86d7f5d3SJohn Marino error (1, 0, "than the %d which Max-dotdot specified",
1042*86d7f5d3SJohn Marino max_dotdot_limit);
1043*86d7f5d3SJohn Marino }
1044*86d7f5d3SJohn Marino }
1045*86d7f5d3SJohn Marino
1046*86d7f5d3SJohn Marino
1047*86d7f5d3SJohn Marino
1048*86d7f5d3SJohn Marino /* Is file or directory REPOS an absolute pathname within the
1049*86d7f5d3SJohn Marino current_parsed_root->directory? If yes, return 0. If no, set pending_error
1050*86d7f5d3SJohn Marino and return 1. */
1051*86d7f5d3SJohn Marino static int
outside_root(char * repos)1052*86d7f5d3SJohn Marino outside_root (char *repos)
1053*86d7f5d3SJohn Marino {
1054*86d7f5d3SJohn Marino size_t repos_len = strlen (repos);
1055*86d7f5d3SJohn Marino size_t root_len = strlen (current_parsed_root->directory);
1056*86d7f5d3SJohn Marino
1057*86d7f5d3SJohn Marino /* ISABSOLUTE (repos) should always be true, but
1058*86d7f5d3SJohn Marino this is a good security precaution regardless. -DRP
1059*86d7f5d3SJohn Marino */
1060*86d7f5d3SJohn Marino if (!ISABSOLUTE (repos))
1061*86d7f5d3SJohn Marino {
1062*86d7f5d3SJohn Marino if (alloc_pending (repos_len + 80))
1063*86d7f5d3SJohn Marino sprintf (pending_error_text, "\
1064*86d7f5d3SJohn Marino E protocol error: %s is not absolute", repos);
1065*86d7f5d3SJohn Marino return 1;
1066*86d7f5d3SJohn Marino }
1067*86d7f5d3SJohn Marino
1068*86d7f5d3SJohn Marino if (repos_len < root_len
1069*86d7f5d3SJohn Marino || strncmp (current_parsed_root->directory, repos, root_len) != 0)
1070*86d7f5d3SJohn Marino {
1071*86d7f5d3SJohn Marino not_within:
1072*86d7f5d3SJohn Marino if (alloc_pending (strlen (current_parsed_root->directory)
1073*86d7f5d3SJohn Marino + strlen (repos)
1074*86d7f5d3SJohn Marino + 80))
1075*86d7f5d3SJohn Marino sprintf (pending_error_text, "\
1076*86d7f5d3SJohn Marino E protocol error: directory '%s' not within root '%s'",
1077*86d7f5d3SJohn Marino repos, current_parsed_root->directory);
1078*86d7f5d3SJohn Marino return 1;
1079*86d7f5d3SJohn Marino }
1080*86d7f5d3SJohn Marino if (repos_len > root_len)
1081*86d7f5d3SJohn Marino {
1082*86d7f5d3SJohn Marino if (repos[root_len] != '/')
1083*86d7f5d3SJohn Marino goto not_within;
1084*86d7f5d3SJohn Marino if (pathname_levels (repos + root_len + 1) > 0)
1085*86d7f5d3SJohn Marino goto not_within;
1086*86d7f5d3SJohn Marino }
1087*86d7f5d3SJohn Marino return 0;
1088*86d7f5d3SJohn Marino }
1089*86d7f5d3SJohn Marino
1090*86d7f5d3SJohn Marino
1091*86d7f5d3SJohn Marino
1092*86d7f5d3SJohn Marino /* Is file or directory FILE outside the current directory (that is, does
1093*86d7f5d3SJohn Marino it contain '/')? If no, return 0. If yes, set pending_error
1094*86d7f5d3SJohn Marino and return 1. */
1095*86d7f5d3SJohn Marino static int
outside_dir(char * file)1096*86d7f5d3SJohn Marino outside_dir (char *file)
1097*86d7f5d3SJohn Marino {
1098*86d7f5d3SJohn Marino if (strchr (file, '/') != NULL)
1099*86d7f5d3SJohn Marino {
1100*86d7f5d3SJohn Marino if (alloc_pending (strlen (file)
1101*86d7f5d3SJohn Marino + 80))
1102*86d7f5d3SJohn Marino sprintf (pending_error_text, "\
1103*86d7f5d3SJohn Marino E protocol error: directory '%s' not within current directory",
1104*86d7f5d3SJohn Marino file);
1105*86d7f5d3SJohn Marino return 1;
1106*86d7f5d3SJohn Marino }
1107*86d7f5d3SJohn Marino return 0;
1108*86d7f5d3SJohn Marino }
1109*86d7f5d3SJohn Marino
1110*86d7f5d3SJohn Marino
1111*86d7f5d3SJohn Marino
1112*86d7f5d3SJohn Marino /*
1113*86d7f5d3SJohn Marino * Add as many directories to the temp directory as the client tells us it
1114*86d7f5d3SJohn Marino * will use "..", so we never try to access something outside the temp
1115*86d7f5d3SJohn Marino * directory via "..".
1116*86d7f5d3SJohn Marino */
1117*86d7f5d3SJohn Marino static void
serve_max_dotdot(char * arg)1118*86d7f5d3SJohn Marino serve_max_dotdot (char *arg)
1119*86d7f5d3SJohn Marino {
1120*86d7f5d3SJohn Marino int lim = atoi (arg);
1121*86d7f5d3SJohn Marino int i;
1122*86d7f5d3SJohn Marino char *p;
1123*86d7f5d3SJohn Marino
1124*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
1125*86d7f5d3SJohn Marino if (proxy_log) return;
1126*86d7f5d3SJohn Marino #endif /* PROXY_SUPPORT */
1127*86d7f5d3SJohn Marino
1128*86d7f5d3SJohn Marino if (lim < 0 || lim > 10000)
1129*86d7f5d3SJohn Marino return;
1130*86d7f5d3SJohn Marino p = xmalloc (strlen (server_temp_dir) + 2 * lim + 10);
1131*86d7f5d3SJohn Marino if (p == NULL)
1132*86d7f5d3SJohn Marino {
1133*86d7f5d3SJohn Marino pending_error = ENOMEM;
1134*86d7f5d3SJohn Marino return;
1135*86d7f5d3SJohn Marino }
1136*86d7f5d3SJohn Marino strcpy (p, server_temp_dir);
1137*86d7f5d3SJohn Marino for (i = 0; i < lim; ++i)
1138*86d7f5d3SJohn Marino strcat (p, "/d");
1139*86d7f5d3SJohn Marino if (server_temp_dir != orig_server_temp_dir)
1140*86d7f5d3SJohn Marino free (server_temp_dir);
1141*86d7f5d3SJohn Marino server_temp_dir = p;
1142*86d7f5d3SJohn Marino max_dotdot_limit = lim;
1143*86d7f5d3SJohn Marino }
1144*86d7f5d3SJohn Marino
1145*86d7f5d3SJohn Marino
1146*86d7f5d3SJohn Marino
1147*86d7f5d3SJohn Marino static char *gDirname;
1148*86d7f5d3SJohn Marino static char *gupdate_dir;
1149*86d7f5d3SJohn Marino
1150*86d7f5d3SJohn Marino static void
dirswitch(char * dir,char * repos)1151*86d7f5d3SJohn Marino dirswitch (char *dir, char *repos)
1152*86d7f5d3SJohn Marino {
1153*86d7f5d3SJohn Marino int status;
1154*86d7f5d3SJohn Marino FILE *f;
1155*86d7f5d3SJohn Marino size_t dir_len;
1156*86d7f5d3SJohn Marino
1157*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "dirswitch (%s, %s)", dir ? dir : "(null)",
1158*86d7f5d3SJohn Marino repos ? repos : "(null)");
1159*86d7f5d3SJohn Marino
1160*86d7f5d3SJohn Marino server_write_entries ();
1161*86d7f5d3SJohn Marino
1162*86d7f5d3SJohn Marino if (error_pending()) return;
1163*86d7f5d3SJohn Marino
1164*86d7f5d3SJohn Marino /* Check for bad directory name.
1165*86d7f5d3SJohn Marino
1166*86d7f5d3SJohn Marino FIXME: could/should unify these checks with server_pathname_check
1167*86d7f5d3SJohn Marino except they need to report errors differently. */
1168*86d7f5d3SJohn Marino if (ISABSOLUTE (dir))
1169*86d7f5d3SJohn Marino {
1170*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (dir)))
1171*86d7f5d3SJohn Marino sprintf ( pending_error_text,
1172*86d7f5d3SJohn Marino "E absolute pathnames invalid for server (specified `%s')",
1173*86d7f5d3SJohn Marino dir);
1174*86d7f5d3SJohn Marino return;
1175*86d7f5d3SJohn Marino }
1176*86d7f5d3SJohn Marino if (pathname_levels (dir) > max_dotdot_limit)
1177*86d7f5d3SJohn Marino {
1178*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (dir)))
1179*86d7f5d3SJohn Marino sprintf (pending_error_text,
1180*86d7f5d3SJohn Marino "E protocol error: `%s' has too many ..", dir);
1181*86d7f5d3SJohn Marino return;
1182*86d7f5d3SJohn Marino }
1183*86d7f5d3SJohn Marino
1184*86d7f5d3SJohn Marino dir_len = strlen (dir);
1185*86d7f5d3SJohn Marino
1186*86d7f5d3SJohn Marino /* Check for a trailing '/'. This is not ISSLASH because \ in the
1187*86d7f5d3SJohn Marino protocol is an ordinary character, not a directory separator (of
1188*86d7f5d3SJohn Marino course, it is perhaps unwise to use it in directory names, but that
1189*86d7f5d3SJohn Marino is another issue). */
1190*86d7f5d3SJohn Marino if (dir_len > 0
1191*86d7f5d3SJohn Marino && dir[dir_len - 1] == '/')
1192*86d7f5d3SJohn Marino {
1193*86d7f5d3SJohn Marino if (alloc_pending (80 + dir_len))
1194*86d7f5d3SJohn Marino sprintf (pending_error_text,
1195*86d7f5d3SJohn Marino "E protocol error: invalid directory syntax in %s", dir);
1196*86d7f5d3SJohn Marino return;
1197*86d7f5d3SJohn Marino }
1198*86d7f5d3SJohn Marino
1199*86d7f5d3SJohn Marino if (gDirname != NULL)
1200*86d7f5d3SJohn Marino free (gDirname);
1201*86d7f5d3SJohn Marino if (gupdate_dir != NULL)
1202*86d7f5d3SJohn Marino free (gupdate_dir);
1203*86d7f5d3SJohn Marino
1204*86d7f5d3SJohn Marino if (!strcmp (dir, "."))
1205*86d7f5d3SJohn Marino gupdate_dir = xstrdup ("");
1206*86d7f5d3SJohn Marino else
1207*86d7f5d3SJohn Marino gupdate_dir = xstrdup (dir);
1208*86d7f5d3SJohn Marino
1209*86d7f5d3SJohn Marino gDirname = xmalloc (strlen (server_temp_dir) + dir_len + 40);
1210*86d7f5d3SJohn Marino if (gDirname == NULL)
1211*86d7f5d3SJohn Marino {
1212*86d7f5d3SJohn Marino pending_error = ENOMEM;
1213*86d7f5d3SJohn Marino return;
1214*86d7f5d3SJohn Marino }
1215*86d7f5d3SJohn Marino
1216*86d7f5d3SJohn Marino strcpy (gDirname, server_temp_dir);
1217*86d7f5d3SJohn Marino strcat (gDirname, "/");
1218*86d7f5d3SJohn Marino strcat (gDirname, dir);
1219*86d7f5d3SJohn Marino
1220*86d7f5d3SJohn Marino status = mkdir_p (gDirname);
1221*86d7f5d3SJohn Marino if (status != 0
1222*86d7f5d3SJohn Marino && status != EEXIST)
1223*86d7f5d3SJohn Marino {
1224*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (gDirname)))
1225*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot mkdir %s", gDirname);
1226*86d7f5d3SJohn Marino pending_error = status;
1227*86d7f5d3SJohn Marino return;
1228*86d7f5d3SJohn Marino }
1229*86d7f5d3SJohn Marino
1230*86d7f5d3SJohn Marino /* We need to create adm directories in all path elements because
1231*86d7f5d3SJohn Marino we want the server to descend them, even if the client hasn't
1232*86d7f5d3SJohn Marino sent the appropriate "Argument xxx" command to match the
1233*86d7f5d3SJohn Marino already-sent "Directory xxx" command. See recurse.c
1234*86d7f5d3SJohn Marino (start_recursion) for a big discussion of this. */
1235*86d7f5d3SJohn Marino
1236*86d7f5d3SJohn Marino status = create_adm_p (server_temp_dir, dir);
1237*86d7f5d3SJohn Marino if (status != 0)
1238*86d7f5d3SJohn Marino {
1239*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (gDirname)))
1240*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot create_adm_p %s", gDirname);
1241*86d7f5d3SJohn Marino pending_error = status;
1242*86d7f5d3SJohn Marino return;
1243*86d7f5d3SJohn Marino }
1244*86d7f5d3SJohn Marino
1245*86d7f5d3SJohn Marino if ( CVS_CHDIR (gDirname) < 0)
1246*86d7f5d3SJohn Marino {
1247*86d7f5d3SJohn Marino int save_errno = errno;
1248*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (gDirname)))
1249*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot change to %s", gDirname);
1250*86d7f5d3SJohn Marino pending_error = save_errno;
1251*86d7f5d3SJohn Marino return;
1252*86d7f5d3SJohn Marino }
1253*86d7f5d3SJohn Marino /*
1254*86d7f5d3SJohn Marino * This is pretty much like calling Create_Admin, but Create_Admin doesn't
1255*86d7f5d3SJohn Marino * report errors in the right way for us.
1256*86d7f5d3SJohn Marino */
1257*86d7f5d3SJohn Marino if ((CVS_MKDIR (CVSADM, 0777) < 0) && (errno != EEXIST))
1258*86d7f5d3SJohn Marino {
1259*86d7f5d3SJohn Marino int save_errno = errno;
1260*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM)))
1261*86d7f5d3SJohn Marino sprintf (pending_error_text,
1262*86d7f5d3SJohn Marino "E cannot mkdir %s/%s", gDirname, CVSADM);
1263*86d7f5d3SJohn Marino pending_error = save_errno;
1264*86d7f5d3SJohn Marino return;
1265*86d7f5d3SJohn Marino }
1266*86d7f5d3SJohn Marino
1267*86d7f5d3SJohn Marino /* The following will overwrite the contents of CVSADM_REP. This
1268*86d7f5d3SJohn Marino is the correct behavior -- mkdir_p may have written a
1269*86d7f5d3SJohn Marino placeholder value to this file and we need to insert the
1270*86d7f5d3SJohn Marino correct value. */
1271*86d7f5d3SJohn Marino
1272*86d7f5d3SJohn Marino f = CVS_FOPEN (CVSADM_REP, "w");
1273*86d7f5d3SJohn Marino if (f == NULL)
1274*86d7f5d3SJohn Marino {
1275*86d7f5d3SJohn Marino int save_errno = errno;
1276*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1277*86d7f5d3SJohn Marino sprintf (pending_error_text,
1278*86d7f5d3SJohn Marino "E cannot open %s/%s", gDirname, CVSADM_REP);
1279*86d7f5d3SJohn Marino pending_error = save_errno;
1280*86d7f5d3SJohn Marino return;
1281*86d7f5d3SJohn Marino }
1282*86d7f5d3SJohn Marino if (fprintf (f, "%s", repos) < 0)
1283*86d7f5d3SJohn Marino {
1284*86d7f5d3SJohn Marino int save_errno = errno;
1285*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1286*86d7f5d3SJohn Marino sprintf (pending_error_text,
1287*86d7f5d3SJohn Marino "E error writing %s/%s", gDirname, CVSADM_REP);
1288*86d7f5d3SJohn Marino pending_error = save_errno;
1289*86d7f5d3SJohn Marino fclose (f);
1290*86d7f5d3SJohn Marino return;
1291*86d7f5d3SJohn Marino }
1292*86d7f5d3SJohn Marino /* Non-remote CVS handles a module representing the entire tree
1293*86d7f5d3SJohn Marino (e.g., an entry like ``world -a .'') by putting /. at the end
1294*86d7f5d3SJohn Marino of the Repository file, so we do the same. */
1295*86d7f5d3SJohn Marino if (strcmp (dir, ".") == 0
1296*86d7f5d3SJohn Marino && current_parsed_root != NULL
1297*86d7f5d3SJohn Marino && current_parsed_root->directory != NULL
1298*86d7f5d3SJohn Marino && strcmp (current_parsed_root->directory, repos) == 0)
1299*86d7f5d3SJohn Marino {
1300*86d7f5d3SJohn Marino if (fprintf (f, "/.") < 0)
1301*86d7f5d3SJohn Marino {
1302*86d7f5d3SJohn Marino int save_errno = errno;
1303*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1304*86d7f5d3SJohn Marino sprintf (pending_error_text,
1305*86d7f5d3SJohn Marino "E error writing %s/%s", gDirname, CVSADM_REP);
1306*86d7f5d3SJohn Marino pending_error = save_errno;
1307*86d7f5d3SJohn Marino fclose (f);
1308*86d7f5d3SJohn Marino return;
1309*86d7f5d3SJohn Marino }
1310*86d7f5d3SJohn Marino }
1311*86d7f5d3SJohn Marino if (fprintf (f, "\n") < 0)
1312*86d7f5d3SJohn Marino {
1313*86d7f5d3SJohn Marino int save_errno = errno;
1314*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1315*86d7f5d3SJohn Marino sprintf (pending_error_text,
1316*86d7f5d3SJohn Marino "E error writing %s/%s", gDirname, CVSADM_REP);
1317*86d7f5d3SJohn Marino pending_error = save_errno;
1318*86d7f5d3SJohn Marino fclose (f);
1319*86d7f5d3SJohn Marino return;
1320*86d7f5d3SJohn Marino }
1321*86d7f5d3SJohn Marino if (fclose (f) == EOF)
1322*86d7f5d3SJohn Marino {
1323*86d7f5d3SJohn Marino int save_errno = errno;
1324*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1325*86d7f5d3SJohn Marino sprintf (pending_error_text,
1326*86d7f5d3SJohn Marino "E error closing %s/%s", gDirname, CVSADM_REP);
1327*86d7f5d3SJohn Marino pending_error = save_errno;
1328*86d7f5d3SJohn Marino return;
1329*86d7f5d3SJohn Marino }
1330*86d7f5d3SJohn Marino /* We open in append mode because we don't want to clobber an
1331*86d7f5d3SJohn Marino existing Entries file. */
1332*86d7f5d3SJohn Marino f = CVS_FOPEN (CVSADM_ENT, "a");
1333*86d7f5d3SJohn Marino if (f == NULL)
1334*86d7f5d3SJohn Marino {
1335*86d7f5d3SJohn Marino int save_errno = errno;
1336*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (CVSADM_ENT)))
1337*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot open %s", CVSADM_ENT);
1338*86d7f5d3SJohn Marino pending_error = save_errno;
1339*86d7f5d3SJohn Marino return;
1340*86d7f5d3SJohn Marino }
1341*86d7f5d3SJohn Marino if (fclose (f) == EOF)
1342*86d7f5d3SJohn Marino {
1343*86d7f5d3SJohn Marino int save_errno = errno;
1344*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (CVSADM_ENT)))
1345*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot close %s", CVSADM_ENT);
1346*86d7f5d3SJohn Marino pending_error = save_errno;
1347*86d7f5d3SJohn Marino return;
1348*86d7f5d3SJohn Marino }
1349*86d7f5d3SJohn Marino }
1350*86d7f5d3SJohn Marino
1351*86d7f5d3SJohn Marino
1352*86d7f5d3SJohn Marino
1353*86d7f5d3SJohn Marino static void
serve_repository(char * arg)1354*86d7f5d3SJohn Marino serve_repository (char *arg)
1355*86d7f5d3SJohn Marino {
1356*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
1357*86d7f5d3SJohn Marino assert (!proxy_log);
1358*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
1359*86d7f5d3SJohn Marino
1360*86d7f5d3SJohn Marino if (alloc_pending (80))
1361*86d7f5d3SJohn Marino strcpy (pending_error_text,
1362*86d7f5d3SJohn Marino "E Repository request is obsolete; aborted");
1363*86d7f5d3SJohn Marino return;
1364*86d7f5d3SJohn Marino }
1365*86d7f5d3SJohn Marino
1366*86d7f5d3SJohn Marino
1367*86d7f5d3SJohn Marino
1368*86d7f5d3SJohn Marino static void
serve_directory(char * arg)1369*86d7f5d3SJohn Marino serve_directory (char *arg)
1370*86d7f5d3SJohn Marino {
1371*86d7f5d3SJohn Marino int status;
1372*86d7f5d3SJohn Marino char *repos;
1373*86d7f5d3SJohn Marino
1374*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "serve_directory (%s)", arg ? arg : "(null)");
1375*86d7f5d3SJohn Marino
1376*86d7f5d3SJohn Marino
1377*86d7f5d3SJohn Marino /* The data needs to be read into the secondary log regardless, but
1378*86d7f5d3SJohn Marino * processing of anything other than errors is skipped until later.
1379*86d7f5d3SJohn Marino */
1380*86d7f5d3SJohn Marino status = buf_read_line (buf_from_net, &repos, NULL);
1381*86d7f5d3SJohn Marino if (status == 0)
1382*86d7f5d3SJohn Marino {
1383*86d7f5d3SJohn Marino if (!ISABSOLUTE (repos))
1384*86d7f5d3SJohn Marino {
1385*86d7f5d3SJohn Marino /* Make absolute.
1386*86d7f5d3SJohn Marino *
1387*86d7f5d3SJohn Marino * FIXME: This is kinda hacky - we should probably only ever store
1388*86d7f5d3SJohn Marino * and pass SHORT_REPOS (perhaps with the occassional exception
1389*86d7f5d3SJohn Marino * for optimizations, but many, many functions end up
1390*86d7f5d3SJohn Marino * deconstructing REPOS to gain SHORT_REPOS anyhow) - the
1391*86d7f5d3SJohn Marino * CVSROOT portion of REPOS is redundant with
1392*86d7f5d3SJohn Marino * current_parsed_root->directory - but since this is the way
1393*86d7f5d3SJohn Marino * things have always been done, changing this will likely involve
1394*86d7f5d3SJohn Marino * a major overhaul.
1395*86d7f5d3SJohn Marino */
1396*86d7f5d3SJohn Marino char *short_repos;
1397*86d7f5d3SJohn Marino
1398*86d7f5d3SJohn Marino short_repos = repos;
1399*86d7f5d3SJohn Marino repos = Xasprintf ("%s/%s",
1400*86d7f5d3SJohn Marino current_parsed_root->directory, short_repos);
1401*86d7f5d3SJohn Marino free (short_repos);
1402*86d7f5d3SJohn Marino }
1403*86d7f5d3SJohn Marino else
1404*86d7f5d3SJohn Marino repos = xstrdup (primary_root_translate (repos));
1405*86d7f5d3SJohn Marino
1406*86d7f5d3SJohn Marino if (
1407*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
1408*86d7f5d3SJohn Marino !proxy_log &&
1409*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
1410*86d7f5d3SJohn Marino !outside_root (repos))
1411*86d7f5d3SJohn Marino dirswitch (arg, repos);
1412*86d7f5d3SJohn Marino free (repos);
1413*86d7f5d3SJohn Marino }
1414*86d7f5d3SJohn Marino else if (status == -2)
1415*86d7f5d3SJohn Marino {
1416*86d7f5d3SJohn Marino pending_error = ENOMEM;
1417*86d7f5d3SJohn Marino }
1418*86d7f5d3SJohn Marino else if (status != 0)
1419*86d7f5d3SJohn Marino {
1420*86d7f5d3SJohn Marino pending_error_text = xmalloc (80 + strlen (arg));
1421*86d7f5d3SJohn Marino if (pending_error_text == NULL)
1422*86d7f5d3SJohn Marino {
1423*86d7f5d3SJohn Marino pending_error = ENOMEM;
1424*86d7f5d3SJohn Marino }
1425*86d7f5d3SJohn Marino else if (status == -1)
1426*86d7f5d3SJohn Marino {
1427*86d7f5d3SJohn Marino sprintf (pending_error_text,
1428*86d7f5d3SJohn Marino "E end of file reading mode for %s", arg);
1429*86d7f5d3SJohn Marino }
1430*86d7f5d3SJohn Marino else
1431*86d7f5d3SJohn Marino {
1432*86d7f5d3SJohn Marino sprintf (pending_error_text,
1433*86d7f5d3SJohn Marino "E error reading mode for %s", arg);
1434*86d7f5d3SJohn Marino pending_error = status;
1435*86d7f5d3SJohn Marino }
1436*86d7f5d3SJohn Marino }
1437*86d7f5d3SJohn Marino }
1438*86d7f5d3SJohn Marino
1439*86d7f5d3SJohn Marino
1440*86d7f5d3SJohn Marino
1441*86d7f5d3SJohn Marino static void
serve_static_directory(char * arg)1442*86d7f5d3SJohn Marino serve_static_directory (char *arg)
1443*86d7f5d3SJohn Marino {
1444*86d7f5d3SJohn Marino FILE *f;
1445*86d7f5d3SJohn Marino
1446*86d7f5d3SJohn Marino if (error_pending ()
1447*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
1448*86d7f5d3SJohn Marino || proxy_log
1449*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
1450*86d7f5d3SJohn Marino ) return;
1451*86d7f5d3SJohn Marino
1452*86d7f5d3SJohn Marino f = CVS_FOPEN (CVSADM_ENTSTAT, "w+");
1453*86d7f5d3SJohn Marino if (f == NULL)
1454*86d7f5d3SJohn Marino {
1455*86d7f5d3SJohn Marino int save_errno = errno;
1456*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (CVSADM_ENTSTAT)))
1457*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot open %s", CVSADM_ENTSTAT);
1458*86d7f5d3SJohn Marino pending_error = save_errno;
1459*86d7f5d3SJohn Marino return;
1460*86d7f5d3SJohn Marino }
1461*86d7f5d3SJohn Marino if (fclose (f) == EOF)
1462*86d7f5d3SJohn Marino {
1463*86d7f5d3SJohn Marino int save_errno = errno;
1464*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (CVSADM_ENTSTAT)))
1465*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot close %s", CVSADM_ENTSTAT);
1466*86d7f5d3SJohn Marino pending_error = save_errno;
1467*86d7f5d3SJohn Marino return;
1468*86d7f5d3SJohn Marino }
1469*86d7f5d3SJohn Marino }
1470*86d7f5d3SJohn Marino
1471*86d7f5d3SJohn Marino
1472*86d7f5d3SJohn Marino
1473*86d7f5d3SJohn Marino static void
serve_sticky(char * arg)1474*86d7f5d3SJohn Marino serve_sticky (char *arg)
1475*86d7f5d3SJohn Marino {
1476*86d7f5d3SJohn Marino FILE *f;
1477*86d7f5d3SJohn Marino
1478*86d7f5d3SJohn Marino if (error_pending ()
1479*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
1480*86d7f5d3SJohn Marino || proxy_log
1481*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
1482*86d7f5d3SJohn Marino ) return;
1483*86d7f5d3SJohn Marino
1484*86d7f5d3SJohn Marino f = CVS_FOPEN (CVSADM_TAG, "w+");
1485*86d7f5d3SJohn Marino if (f == NULL)
1486*86d7f5d3SJohn Marino {
1487*86d7f5d3SJohn Marino int save_errno = errno;
1488*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (CVSADM_TAG)))
1489*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot open %s", CVSADM_TAG);
1490*86d7f5d3SJohn Marino pending_error = save_errno;
1491*86d7f5d3SJohn Marino return;
1492*86d7f5d3SJohn Marino }
1493*86d7f5d3SJohn Marino if (fprintf (f, "%s\n", arg) < 0)
1494*86d7f5d3SJohn Marino {
1495*86d7f5d3SJohn Marino int save_errno = errno;
1496*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (CVSADM_TAG)))
1497*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot write to %s", CVSADM_TAG);
1498*86d7f5d3SJohn Marino pending_error = save_errno;
1499*86d7f5d3SJohn Marino return;
1500*86d7f5d3SJohn Marino }
1501*86d7f5d3SJohn Marino if (fclose (f) == EOF)
1502*86d7f5d3SJohn Marino {
1503*86d7f5d3SJohn Marino int save_errno = errno;
1504*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (CVSADM_TAG)))
1505*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot close %s", CVSADM_TAG);
1506*86d7f5d3SJohn Marino pending_error = save_errno;
1507*86d7f5d3SJohn Marino return;
1508*86d7f5d3SJohn Marino }
1509*86d7f5d3SJohn Marino }
1510*86d7f5d3SJohn Marino
1511*86d7f5d3SJohn Marino
1512*86d7f5d3SJohn Marino
1513*86d7f5d3SJohn Marino /*
1514*86d7f5d3SJohn Marino * Read SIZE bytes from buf_from_net, write them to FILE.
1515*86d7f5d3SJohn Marino *
1516*86d7f5d3SJohn Marino * Currently this isn't really used for receiving parts of a file --
1517*86d7f5d3SJohn Marino * the file is still sent over in one chunk. But if/when we get
1518*86d7f5d3SJohn Marino * spiffy in-process gzip support working, perhaps the compressed
1519*86d7f5d3SJohn Marino * pieces could be sent over as they're ready, if the network is fast
1520*86d7f5d3SJohn Marino * enough. Or something.
1521*86d7f5d3SJohn Marino */
1522*86d7f5d3SJohn Marino static void
receive_partial_file(size_t size,int file)1523*86d7f5d3SJohn Marino receive_partial_file (size_t size, int file)
1524*86d7f5d3SJohn Marino {
1525*86d7f5d3SJohn Marino while (size > 0)
1526*86d7f5d3SJohn Marino {
1527*86d7f5d3SJohn Marino int status;
1528*86d7f5d3SJohn Marino size_t nread;
1529*86d7f5d3SJohn Marino char *data;
1530*86d7f5d3SJohn Marino
1531*86d7f5d3SJohn Marino status = buf_read_data (buf_from_net, size, &data, &nread);
1532*86d7f5d3SJohn Marino if (status != 0)
1533*86d7f5d3SJohn Marino {
1534*86d7f5d3SJohn Marino if (status == -2)
1535*86d7f5d3SJohn Marino pending_error = ENOMEM;
1536*86d7f5d3SJohn Marino else
1537*86d7f5d3SJohn Marino {
1538*86d7f5d3SJohn Marino pending_error_text = xmalloc (80);
1539*86d7f5d3SJohn Marino if (pending_error_text == NULL)
1540*86d7f5d3SJohn Marino pending_error = ENOMEM;
1541*86d7f5d3SJohn Marino else if (status == -1)
1542*86d7f5d3SJohn Marino {
1543*86d7f5d3SJohn Marino sprintf (pending_error_text,
1544*86d7f5d3SJohn Marino "E premature end of file from client");
1545*86d7f5d3SJohn Marino pending_error = 0;
1546*86d7f5d3SJohn Marino }
1547*86d7f5d3SJohn Marino else
1548*86d7f5d3SJohn Marino {
1549*86d7f5d3SJohn Marino sprintf (pending_error_text,
1550*86d7f5d3SJohn Marino "E error reading from client");
1551*86d7f5d3SJohn Marino pending_error = status;
1552*86d7f5d3SJohn Marino }
1553*86d7f5d3SJohn Marino }
1554*86d7f5d3SJohn Marino return;
1555*86d7f5d3SJohn Marino }
1556*86d7f5d3SJohn Marino
1557*86d7f5d3SJohn Marino size -= nread;
1558*86d7f5d3SJohn Marino
1559*86d7f5d3SJohn Marino while (nread > 0)
1560*86d7f5d3SJohn Marino {
1561*86d7f5d3SJohn Marino ssize_t nwrote;
1562*86d7f5d3SJohn Marino
1563*86d7f5d3SJohn Marino nwrote = write (file, data, nread);
1564*86d7f5d3SJohn Marino if (nwrote < 0)
1565*86d7f5d3SJohn Marino {
1566*86d7f5d3SJohn Marino int save_errno = errno;
1567*86d7f5d3SJohn Marino if (alloc_pending (40))
1568*86d7f5d3SJohn Marino strcpy (pending_error_text, "E unable to write");
1569*86d7f5d3SJohn Marino pending_error = save_errno;
1570*86d7f5d3SJohn Marino
1571*86d7f5d3SJohn Marino /* Read and discard the file data. */
1572*86d7f5d3SJohn Marino while (size > 0)
1573*86d7f5d3SJohn Marino {
1574*86d7f5d3SJohn Marino int status;
1575*86d7f5d3SJohn Marino size_t nread;
1576*86d7f5d3SJohn Marino char *data;
1577*86d7f5d3SJohn Marino
1578*86d7f5d3SJohn Marino status = buf_read_data (buf_from_net, size, &data, &nread);
1579*86d7f5d3SJohn Marino if (status != 0)
1580*86d7f5d3SJohn Marino return;
1581*86d7f5d3SJohn Marino size -= nread;
1582*86d7f5d3SJohn Marino }
1583*86d7f5d3SJohn Marino
1584*86d7f5d3SJohn Marino return;
1585*86d7f5d3SJohn Marino }
1586*86d7f5d3SJohn Marino nread -= nwrote;
1587*86d7f5d3SJohn Marino data += nwrote;
1588*86d7f5d3SJohn Marino }
1589*86d7f5d3SJohn Marino }
1590*86d7f5d3SJohn Marino }
1591*86d7f5d3SJohn Marino
1592*86d7f5d3SJohn Marino
1593*86d7f5d3SJohn Marino
1594*86d7f5d3SJohn Marino /* Receive SIZE bytes, write to filename FILE. */
1595*86d7f5d3SJohn Marino static void
receive_file(size_t size,char * file,int gzipped)1596*86d7f5d3SJohn Marino receive_file (size_t size, char *file, int gzipped)
1597*86d7f5d3SJohn Marino {
1598*86d7f5d3SJohn Marino int fd;
1599*86d7f5d3SJohn Marino char *arg = file;
1600*86d7f5d3SJohn Marino
1601*86d7f5d3SJohn Marino /* Write the file. */
1602*86d7f5d3SJohn Marino fd = CVS_OPEN (arg, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1603*86d7f5d3SJohn Marino if (fd < 0)
1604*86d7f5d3SJohn Marino {
1605*86d7f5d3SJohn Marino int save_errno = errno;
1606*86d7f5d3SJohn Marino if (alloc_pending (40 + strlen (arg)))
1607*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot open %s", arg);
1608*86d7f5d3SJohn Marino pending_error = save_errno;
1609*86d7f5d3SJohn Marino return;
1610*86d7f5d3SJohn Marino }
1611*86d7f5d3SJohn Marino
1612*86d7f5d3SJohn Marino if (gzipped)
1613*86d7f5d3SJohn Marino {
1614*86d7f5d3SJohn Marino /* Using gunzip_and_write isn't really a high-performance
1615*86d7f5d3SJohn Marino approach, because it keeps the whole thing in memory
1616*86d7f5d3SJohn Marino (contiguous memory, worse yet). But it seems easier to
1617*86d7f5d3SJohn Marino code than the alternative (and less vulnerable to subtle
1618*86d7f5d3SJohn Marino bugs). Given that this feature is mainly for
1619*86d7f5d3SJohn Marino compatibility, that is the better tradeoff. */
1620*86d7f5d3SJohn Marino
1621*86d7f5d3SJohn Marino size_t toread = size;
1622*86d7f5d3SJohn Marino char *filebuf;
1623*86d7f5d3SJohn Marino char *p;
1624*86d7f5d3SJohn Marino
1625*86d7f5d3SJohn Marino filebuf = xmalloc (size);
1626*86d7f5d3SJohn Marino p = filebuf;
1627*86d7f5d3SJohn Marino /* If NULL, we still want to read the data and discard it. */
1628*86d7f5d3SJohn Marino
1629*86d7f5d3SJohn Marino while (toread > 0)
1630*86d7f5d3SJohn Marino {
1631*86d7f5d3SJohn Marino int status;
1632*86d7f5d3SJohn Marino size_t nread;
1633*86d7f5d3SJohn Marino char *data;
1634*86d7f5d3SJohn Marino
1635*86d7f5d3SJohn Marino status = buf_read_data (buf_from_net, toread, &data, &nread);
1636*86d7f5d3SJohn Marino if (status != 0)
1637*86d7f5d3SJohn Marino {
1638*86d7f5d3SJohn Marino if (status == -2)
1639*86d7f5d3SJohn Marino pending_error = ENOMEM;
1640*86d7f5d3SJohn Marino else
1641*86d7f5d3SJohn Marino {
1642*86d7f5d3SJohn Marino pending_error_text = xmalloc (80);
1643*86d7f5d3SJohn Marino if (pending_error_text == NULL)
1644*86d7f5d3SJohn Marino pending_error = ENOMEM;
1645*86d7f5d3SJohn Marino else if (status == -1)
1646*86d7f5d3SJohn Marino {
1647*86d7f5d3SJohn Marino sprintf (pending_error_text,
1648*86d7f5d3SJohn Marino "E premature end of file from client");
1649*86d7f5d3SJohn Marino pending_error = 0;
1650*86d7f5d3SJohn Marino }
1651*86d7f5d3SJohn Marino else
1652*86d7f5d3SJohn Marino {
1653*86d7f5d3SJohn Marino sprintf (pending_error_text,
1654*86d7f5d3SJohn Marino "E error reading from client");
1655*86d7f5d3SJohn Marino pending_error = status;
1656*86d7f5d3SJohn Marino }
1657*86d7f5d3SJohn Marino }
1658*86d7f5d3SJohn Marino return;
1659*86d7f5d3SJohn Marino }
1660*86d7f5d3SJohn Marino
1661*86d7f5d3SJohn Marino toread -= nread;
1662*86d7f5d3SJohn Marino
1663*86d7f5d3SJohn Marino if (filebuf != NULL)
1664*86d7f5d3SJohn Marino {
1665*86d7f5d3SJohn Marino memcpy (p, data, nread);
1666*86d7f5d3SJohn Marino p += nread;
1667*86d7f5d3SJohn Marino }
1668*86d7f5d3SJohn Marino }
1669*86d7f5d3SJohn Marino if (filebuf == NULL)
1670*86d7f5d3SJohn Marino {
1671*86d7f5d3SJohn Marino pending_error = ENOMEM;
1672*86d7f5d3SJohn Marino goto out;
1673*86d7f5d3SJohn Marino }
1674*86d7f5d3SJohn Marino
1675*86d7f5d3SJohn Marino if (gunzip_and_write (fd, file, (unsigned char *) filebuf, size))
1676*86d7f5d3SJohn Marino {
1677*86d7f5d3SJohn Marino if (alloc_pending (80))
1678*86d7f5d3SJohn Marino sprintf (pending_error_text,
1679*86d7f5d3SJohn Marino "E aborting due to compression error");
1680*86d7f5d3SJohn Marino }
1681*86d7f5d3SJohn Marino free (filebuf);
1682*86d7f5d3SJohn Marino }
1683*86d7f5d3SJohn Marino else
1684*86d7f5d3SJohn Marino receive_partial_file (size, fd);
1685*86d7f5d3SJohn Marino
1686*86d7f5d3SJohn Marino if (pending_error_text)
1687*86d7f5d3SJohn Marino {
1688*86d7f5d3SJohn Marino char *p = xrealloc (pending_error_text,
1689*86d7f5d3SJohn Marino strlen (pending_error_text) + strlen (arg) + 30);
1690*86d7f5d3SJohn Marino if (p)
1691*86d7f5d3SJohn Marino {
1692*86d7f5d3SJohn Marino pending_error_text = p;
1693*86d7f5d3SJohn Marino sprintf (p + strlen (p), ", file %s", arg);
1694*86d7f5d3SJohn Marino }
1695*86d7f5d3SJohn Marino /* else original string is supposed to be unchanged */
1696*86d7f5d3SJohn Marino }
1697*86d7f5d3SJohn Marino
1698*86d7f5d3SJohn Marino out:
1699*86d7f5d3SJohn Marino if (close (fd) < 0 && !error_pending ())
1700*86d7f5d3SJohn Marino {
1701*86d7f5d3SJohn Marino int save_errno = errno;
1702*86d7f5d3SJohn Marino if (alloc_pending (40 + strlen (arg)))
1703*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot close %s", arg);
1704*86d7f5d3SJohn Marino pending_error = save_errno;
1705*86d7f5d3SJohn Marino return;
1706*86d7f5d3SJohn Marino }
1707*86d7f5d3SJohn Marino }
1708*86d7f5d3SJohn Marino
1709*86d7f5d3SJohn Marino
1710*86d7f5d3SJohn Marino
1711*86d7f5d3SJohn Marino /* Kopt for the next file sent in Modified or Is-modified. */
1712*86d7f5d3SJohn Marino static char *kopt;
1713*86d7f5d3SJohn Marino
1714*86d7f5d3SJohn Marino /* Timestamp (Checkin-time) for next file sent in Modified or
1715*86d7f5d3SJohn Marino Is-modified. */
1716*86d7f5d3SJohn Marino static int checkin_time_valid;
1717*86d7f5d3SJohn Marino static time_t checkin_time;
1718*86d7f5d3SJohn Marino
1719*86d7f5d3SJohn Marino
1720*86d7f5d3SJohn Marino
1721*86d7f5d3SJohn Marino /*
1722*86d7f5d3SJohn Marino * Used to keep track of Entry requests.
1723*86d7f5d3SJohn Marino */
1724*86d7f5d3SJohn Marino struct an_entry {
1725*86d7f5d3SJohn Marino struct an_entry *next;
1726*86d7f5d3SJohn Marino char *entry;
1727*86d7f5d3SJohn Marino };
1728*86d7f5d3SJohn Marino
1729*86d7f5d3SJohn Marino static struct an_entry *entries;
1730*86d7f5d3SJohn Marino
1731*86d7f5d3SJohn Marino static void
serve_is_modified(char * arg)1732*86d7f5d3SJohn Marino serve_is_modified (char *arg)
1733*86d7f5d3SJohn Marino {
1734*86d7f5d3SJohn Marino struct an_entry *p;
1735*86d7f5d3SJohn Marino char *name;
1736*86d7f5d3SJohn Marino char *cp;
1737*86d7f5d3SJohn Marino char *timefield;
1738*86d7f5d3SJohn Marino /* Have we found this file in "entries" yet. */
1739*86d7f5d3SJohn Marino int found;
1740*86d7f5d3SJohn Marino
1741*86d7f5d3SJohn Marino if (error_pending ()
1742*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
1743*86d7f5d3SJohn Marino || proxy_log
1744*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
1745*86d7f5d3SJohn Marino ) return;
1746*86d7f5d3SJohn Marino
1747*86d7f5d3SJohn Marino if (outside_dir (arg))
1748*86d7f5d3SJohn Marino return;
1749*86d7f5d3SJohn Marino
1750*86d7f5d3SJohn Marino /* Rewrite entries file to have `M' in timestamp field. */
1751*86d7f5d3SJohn Marino found = 0;
1752*86d7f5d3SJohn Marino for (p = entries; p != NULL; p = p->next)
1753*86d7f5d3SJohn Marino {
1754*86d7f5d3SJohn Marino name = p->entry + 1;
1755*86d7f5d3SJohn Marino cp = strchr (name, '/');
1756*86d7f5d3SJohn Marino if (cp != NULL
1757*86d7f5d3SJohn Marino && strlen (arg) == cp - name
1758*86d7f5d3SJohn Marino && strncmp (arg, name, cp - name) == 0)
1759*86d7f5d3SJohn Marino {
1760*86d7f5d3SJohn Marino if (!(timefield = strchr (cp + 1, '/')) || *++timefield == '\0')
1761*86d7f5d3SJohn Marino {
1762*86d7f5d3SJohn Marino /* We didn't find the record separator or it is followed by
1763*86d7f5d3SJohn Marino * the end of the string, so just exit.
1764*86d7f5d3SJohn Marino */
1765*86d7f5d3SJohn Marino if (alloc_pending (80))
1766*86d7f5d3SJohn Marino sprintf (pending_error_text,
1767*86d7f5d3SJohn Marino "E Malformed Entry encountered.");
1768*86d7f5d3SJohn Marino return;
1769*86d7f5d3SJohn Marino }
1770*86d7f5d3SJohn Marino /* If the time field is not currently empty, then one of
1771*86d7f5d3SJohn Marino * serve_modified, serve_is_modified, & serve_unchanged were
1772*86d7f5d3SJohn Marino * already called for this file. We would like to ignore the
1773*86d7f5d3SJohn Marino * reinvocation silently or, better yet, exit with an error
1774*86d7f5d3SJohn Marino * message, but we just avoid the copy-forward and overwrite the
1775*86d7f5d3SJohn Marino * value from the last invocation instead. See the comment below
1776*86d7f5d3SJohn Marino * for more.
1777*86d7f5d3SJohn Marino */
1778*86d7f5d3SJohn Marino if (*timefield == '/')
1779*86d7f5d3SJohn Marino {
1780*86d7f5d3SJohn Marino /* Copy forward one character. Space was allocated for this
1781*86d7f5d3SJohn Marino * already in serve_entry(). */
1782*86d7f5d3SJohn Marino cp = timefield + strlen (timefield);
1783*86d7f5d3SJohn Marino cp[1] = '\0';
1784*86d7f5d3SJohn Marino while (cp > timefield)
1785*86d7f5d3SJohn Marino {
1786*86d7f5d3SJohn Marino *cp = cp[-1];
1787*86d7f5d3SJohn Marino --cp;
1788*86d7f5d3SJohn Marino }
1789*86d7f5d3SJohn Marino
1790*86d7f5d3SJohn Marino /* *timefield == '/'; */
1791*86d7f5d3SJohn Marino }
1792*86d7f5d3SJohn Marino /* If *TIMEFIELD wasn't '/' and wasn't '+', we assume that it was
1793*86d7f5d3SJohn Marino * because of multiple calls to Is-modified & Unchanged by the
1794*86d7f5d3SJohn Marino * client and just overwrite the value from the last call.
1795*86d7f5d3SJohn Marino * Technically, we should probably either ignore calls after the
1796*86d7f5d3SJohn Marino * first or send the client an error, since the client/server
1797*86d7f5d3SJohn Marino * protocol specification specifies that only one call to either
1798*86d7f5d3SJohn Marino * Is-Modified or Unchanged is allowed, but broken versions of
1799*86d7f5d3SJohn Marino * CVSNT (at least 2.0.34 - 2.0.41, reported fixed in 2.0.41a) and
1800*86d7f5d3SJohn Marino * the WinCVS & TortoiseCVS clients which depend on those broken
1801*86d7f5d3SJohn Marino * versions of CVSNT (WinCVS 1.3 & at least one TortoiseCVS
1802*86d7f5d3SJohn Marino * release) rely on this behavior.
1803*86d7f5d3SJohn Marino */
1804*86d7f5d3SJohn Marino if (*timefield != '+')
1805*86d7f5d3SJohn Marino *timefield = 'M';
1806*86d7f5d3SJohn Marino
1807*86d7f5d3SJohn Marino if (kopt != NULL)
1808*86d7f5d3SJohn Marino {
1809*86d7f5d3SJohn Marino if (alloc_pending (strlen (name) + 80))
1810*86d7f5d3SJohn Marino sprintf (pending_error_text,
1811*86d7f5d3SJohn Marino "E protocol error: both Kopt and Entry for %s",
1812*86d7f5d3SJohn Marino arg);
1813*86d7f5d3SJohn Marino free (kopt);
1814*86d7f5d3SJohn Marino kopt = NULL;
1815*86d7f5d3SJohn Marino return;
1816*86d7f5d3SJohn Marino }
1817*86d7f5d3SJohn Marino found = 1;
1818*86d7f5d3SJohn Marino break;
1819*86d7f5d3SJohn Marino }
1820*86d7f5d3SJohn Marino }
1821*86d7f5d3SJohn Marino if (!found)
1822*86d7f5d3SJohn Marino {
1823*86d7f5d3SJohn Marino /* We got Is-modified but no Entry. Add a dummy entry.
1824*86d7f5d3SJohn Marino The "D" timestamp is what makes it a dummy. */
1825*86d7f5d3SJohn Marino p = xmalloc (sizeof (struct an_entry));
1826*86d7f5d3SJohn Marino if (p == NULL)
1827*86d7f5d3SJohn Marino {
1828*86d7f5d3SJohn Marino pending_error = ENOMEM;
1829*86d7f5d3SJohn Marino return;
1830*86d7f5d3SJohn Marino }
1831*86d7f5d3SJohn Marino p->entry = xmalloc (strlen (arg) + 80);
1832*86d7f5d3SJohn Marino if (p->entry == NULL)
1833*86d7f5d3SJohn Marino {
1834*86d7f5d3SJohn Marino pending_error = ENOMEM;
1835*86d7f5d3SJohn Marino free (p);
1836*86d7f5d3SJohn Marino return;
1837*86d7f5d3SJohn Marino }
1838*86d7f5d3SJohn Marino strcpy (p->entry, "/");
1839*86d7f5d3SJohn Marino strcat (p->entry, arg);
1840*86d7f5d3SJohn Marino strcat (p->entry, "//D/");
1841*86d7f5d3SJohn Marino if (kopt != NULL)
1842*86d7f5d3SJohn Marino {
1843*86d7f5d3SJohn Marino strcat (p->entry, kopt);
1844*86d7f5d3SJohn Marino free (kopt);
1845*86d7f5d3SJohn Marino kopt = NULL;
1846*86d7f5d3SJohn Marino }
1847*86d7f5d3SJohn Marino strcat (p->entry, "/");
1848*86d7f5d3SJohn Marino p->next = entries;
1849*86d7f5d3SJohn Marino entries = p;
1850*86d7f5d3SJohn Marino }
1851*86d7f5d3SJohn Marino }
1852*86d7f5d3SJohn Marino
1853*86d7f5d3SJohn Marino
1854*86d7f5d3SJohn Marino
1855*86d7f5d3SJohn Marino static void
serve_modified(char * arg)1856*86d7f5d3SJohn Marino serve_modified (char *arg)
1857*86d7f5d3SJohn Marino {
1858*86d7f5d3SJohn Marino size_t size;
1859*86d7f5d3SJohn Marino int read_size;
1860*86d7f5d3SJohn Marino int status;
1861*86d7f5d3SJohn Marino char *size_text;
1862*86d7f5d3SJohn Marino char *mode_text;
1863*86d7f5d3SJohn Marino
1864*86d7f5d3SJohn Marino int gzipped = 0;
1865*86d7f5d3SJohn Marino
1866*86d7f5d3SJohn Marino /*
1867*86d7f5d3SJohn Marino * This used to return immediately if error_pending () was true.
1868*86d7f5d3SJohn Marino * However, that fails, because it causes each line of the file to
1869*86d7f5d3SJohn Marino * be echoed back to the client as an unrecognized command. The
1870*86d7f5d3SJohn Marino * client isn't reading from the socket, so eventually both
1871*86d7f5d3SJohn Marino * processes block trying to write to the other. Now, we try to
1872*86d7f5d3SJohn Marino * read the file if we can.
1873*86d7f5d3SJohn Marino */
1874*86d7f5d3SJohn Marino
1875*86d7f5d3SJohn Marino status = buf_read_line (buf_from_net, &mode_text, NULL);
1876*86d7f5d3SJohn Marino if (status != 0)
1877*86d7f5d3SJohn Marino {
1878*86d7f5d3SJohn Marino if (status == -2)
1879*86d7f5d3SJohn Marino pending_error = ENOMEM;
1880*86d7f5d3SJohn Marino else
1881*86d7f5d3SJohn Marino {
1882*86d7f5d3SJohn Marino pending_error_text = xmalloc (80 + strlen (arg));
1883*86d7f5d3SJohn Marino if (pending_error_text == NULL)
1884*86d7f5d3SJohn Marino pending_error = ENOMEM;
1885*86d7f5d3SJohn Marino else
1886*86d7f5d3SJohn Marino {
1887*86d7f5d3SJohn Marino if (status == -1)
1888*86d7f5d3SJohn Marino sprintf (pending_error_text,
1889*86d7f5d3SJohn Marino "E end of file reading mode for %s", arg);
1890*86d7f5d3SJohn Marino else
1891*86d7f5d3SJohn Marino {
1892*86d7f5d3SJohn Marino sprintf (pending_error_text,
1893*86d7f5d3SJohn Marino "E error reading mode for %s", arg);
1894*86d7f5d3SJohn Marino pending_error = status;
1895*86d7f5d3SJohn Marino }
1896*86d7f5d3SJohn Marino }
1897*86d7f5d3SJohn Marino }
1898*86d7f5d3SJohn Marino return;
1899*86d7f5d3SJohn Marino }
1900*86d7f5d3SJohn Marino
1901*86d7f5d3SJohn Marino status = buf_read_line (buf_from_net, &size_text, NULL);
1902*86d7f5d3SJohn Marino if (status != 0)
1903*86d7f5d3SJohn Marino {
1904*86d7f5d3SJohn Marino if (status == -2)
1905*86d7f5d3SJohn Marino pending_error = ENOMEM;
1906*86d7f5d3SJohn Marino else
1907*86d7f5d3SJohn Marino {
1908*86d7f5d3SJohn Marino pending_error_text = xmalloc (80 + strlen (arg));
1909*86d7f5d3SJohn Marino if (pending_error_text == NULL)
1910*86d7f5d3SJohn Marino pending_error = ENOMEM;
1911*86d7f5d3SJohn Marino else
1912*86d7f5d3SJohn Marino {
1913*86d7f5d3SJohn Marino if (status == -1)
1914*86d7f5d3SJohn Marino sprintf (pending_error_text,
1915*86d7f5d3SJohn Marino "E end of file reading size for %s", arg);
1916*86d7f5d3SJohn Marino else
1917*86d7f5d3SJohn Marino {
1918*86d7f5d3SJohn Marino sprintf (pending_error_text,
1919*86d7f5d3SJohn Marino "E error reading size for %s", arg);
1920*86d7f5d3SJohn Marino pending_error = status;
1921*86d7f5d3SJohn Marino }
1922*86d7f5d3SJohn Marino }
1923*86d7f5d3SJohn Marino }
1924*86d7f5d3SJohn Marino free (mode_text);
1925*86d7f5d3SJohn Marino return;
1926*86d7f5d3SJohn Marino }
1927*86d7f5d3SJohn Marino if (size_text[0] == 'z')
1928*86d7f5d3SJohn Marino {
1929*86d7f5d3SJohn Marino gzipped = 1;
1930*86d7f5d3SJohn Marino read_size = atoi (size_text + 1);
1931*86d7f5d3SJohn Marino }
1932*86d7f5d3SJohn Marino else
1933*86d7f5d3SJohn Marino read_size = atoi (size_text);
1934*86d7f5d3SJohn Marino free (size_text);
1935*86d7f5d3SJohn Marino
1936*86d7f5d3SJohn Marino if (read_size < 0 && alloc_pending (80))
1937*86d7f5d3SJohn Marino {
1938*86d7f5d3SJohn Marino sprintf (pending_error_text,
1939*86d7f5d3SJohn Marino "E client sent invalid (negative) file size");
1940*86d7f5d3SJohn Marino return;
1941*86d7f5d3SJohn Marino }
1942*86d7f5d3SJohn Marino else
1943*86d7f5d3SJohn Marino size = read_size;
1944*86d7f5d3SJohn Marino
1945*86d7f5d3SJohn Marino if (error_pending ())
1946*86d7f5d3SJohn Marino {
1947*86d7f5d3SJohn Marino /* Now that we know the size, read and discard the file data. */
1948*86d7f5d3SJohn Marino while (size > 0)
1949*86d7f5d3SJohn Marino {
1950*86d7f5d3SJohn Marino int status;
1951*86d7f5d3SJohn Marino size_t nread;
1952*86d7f5d3SJohn Marino char *data;
1953*86d7f5d3SJohn Marino
1954*86d7f5d3SJohn Marino status = buf_read_data (buf_from_net, size, &data, &nread);
1955*86d7f5d3SJohn Marino if (status != 0)
1956*86d7f5d3SJohn Marino return;
1957*86d7f5d3SJohn Marino size -= nread;
1958*86d7f5d3SJohn Marino }
1959*86d7f5d3SJohn Marino free (mode_text);
1960*86d7f5d3SJohn Marino return;
1961*86d7f5d3SJohn Marino }
1962*86d7f5d3SJohn Marino
1963*86d7f5d3SJohn Marino if (
1964*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
1965*86d7f5d3SJohn Marino !proxy_log &&
1966*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
1967*86d7f5d3SJohn Marino outside_dir (arg))
1968*86d7f5d3SJohn Marino {
1969*86d7f5d3SJohn Marino free (mode_text);
1970*86d7f5d3SJohn Marino return;
1971*86d7f5d3SJohn Marino }
1972*86d7f5d3SJohn Marino
1973*86d7f5d3SJohn Marino receive_file (size,
1974*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
1975*86d7f5d3SJohn Marino proxy_log ? DEVNULL :
1976*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
1977*86d7f5d3SJohn Marino arg,
1978*86d7f5d3SJohn Marino gzipped);
1979*86d7f5d3SJohn Marino if (error_pending ())
1980*86d7f5d3SJohn Marino {
1981*86d7f5d3SJohn Marino free (mode_text);
1982*86d7f5d3SJohn Marino return;
1983*86d7f5d3SJohn Marino }
1984*86d7f5d3SJohn Marino
1985*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
1986*86d7f5d3SJohn Marino /* We've read all the data that needed to be read if we're still logging
1987*86d7f5d3SJohn Marino * for a secondary. Return.
1988*86d7f5d3SJohn Marino */
1989*86d7f5d3SJohn Marino if (proxy_log) return;
1990*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
1991*86d7f5d3SJohn Marino
1992*86d7f5d3SJohn Marino if (checkin_time_valid)
1993*86d7f5d3SJohn Marino {
1994*86d7f5d3SJohn Marino struct utimbuf t;
1995*86d7f5d3SJohn Marino
1996*86d7f5d3SJohn Marino memset (&t, 0, sizeof (t));
1997*86d7f5d3SJohn Marino t.modtime = t.actime = checkin_time;
1998*86d7f5d3SJohn Marino if (utime (arg, &t) < 0)
1999*86d7f5d3SJohn Marino {
2000*86d7f5d3SJohn Marino int save_errno = errno;
2001*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (arg)))
2002*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot utime %s", arg);
2003*86d7f5d3SJohn Marino pending_error = save_errno;
2004*86d7f5d3SJohn Marino free (mode_text);
2005*86d7f5d3SJohn Marino return;
2006*86d7f5d3SJohn Marino }
2007*86d7f5d3SJohn Marino checkin_time_valid = 0;
2008*86d7f5d3SJohn Marino }
2009*86d7f5d3SJohn Marino
2010*86d7f5d3SJohn Marino {
2011*86d7f5d3SJohn Marino int status = change_mode (arg, mode_text, 0);
2012*86d7f5d3SJohn Marino free (mode_text);
2013*86d7f5d3SJohn Marino if (status)
2014*86d7f5d3SJohn Marino {
2015*86d7f5d3SJohn Marino if (alloc_pending (40 + strlen (arg)))
2016*86d7f5d3SJohn Marino sprintf (pending_error_text,
2017*86d7f5d3SJohn Marino "E cannot change mode for %s", arg);
2018*86d7f5d3SJohn Marino pending_error = status;
2019*86d7f5d3SJohn Marino return;
2020*86d7f5d3SJohn Marino }
2021*86d7f5d3SJohn Marino }
2022*86d7f5d3SJohn Marino
2023*86d7f5d3SJohn Marino /* Make sure that the Entries indicate the right kopt. We probably
2024*86d7f5d3SJohn Marino could do this even in the non-kopt case and, I think, save a stat()
2025*86d7f5d3SJohn Marino call in time_stamp_server. But for conservatism I'm leaving the
2026*86d7f5d3SJohn Marino non-kopt case alone. */
2027*86d7f5d3SJohn Marino if (kopt != NULL)
2028*86d7f5d3SJohn Marino serve_is_modified (arg);
2029*86d7f5d3SJohn Marino }
2030*86d7f5d3SJohn Marino
2031*86d7f5d3SJohn Marino
2032*86d7f5d3SJohn Marino
2033*86d7f5d3SJohn Marino static void
serve_enable_unchanged(char * arg)2034*86d7f5d3SJohn Marino serve_enable_unchanged (char *arg)
2035*86d7f5d3SJohn Marino {
2036*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
2037*86d7f5d3SJohn Marino /* Might as well skip this since this function does nothing anyhow. If
2038*86d7f5d3SJohn Marino * it did do anything and could generate errors, then the line below would
2039*86d7f5d3SJohn Marino * be necessary since this can be processed before a `Root' request.
2040*86d7f5d3SJohn Marino *
2041*86d7f5d3SJohn Marino * if (reprocessing) return;
2042*86d7f5d3SJohn Marino */
2043*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
2044*86d7f5d3SJohn Marino }
2045*86d7f5d3SJohn Marino
2046*86d7f5d3SJohn Marino
2047*86d7f5d3SJohn Marino
2048*86d7f5d3SJohn Marino static void
serve_unchanged(char * arg)2049*86d7f5d3SJohn Marino serve_unchanged (char *arg)
2050*86d7f5d3SJohn Marino {
2051*86d7f5d3SJohn Marino struct an_entry *p;
2052*86d7f5d3SJohn Marino char *name;
2053*86d7f5d3SJohn Marino char *cp;
2054*86d7f5d3SJohn Marino char *timefield;
2055*86d7f5d3SJohn Marino
2056*86d7f5d3SJohn Marino if (error_pending ()
2057*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
2058*86d7f5d3SJohn Marino || proxy_log
2059*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
2060*86d7f5d3SJohn Marino ) return;
2061*86d7f5d3SJohn Marino
2062*86d7f5d3SJohn Marino if (outside_dir (arg))
2063*86d7f5d3SJohn Marino return;
2064*86d7f5d3SJohn Marino
2065*86d7f5d3SJohn Marino /* Rewrite entries file to have `=' in timestamp field. */
2066*86d7f5d3SJohn Marino for (p = entries; p != NULL; p = p->next)
2067*86d7f5d3SJohn Marino {
2068*86d7f5d3SJohn Marino name = p->entry + 1;
2069*86d7f5d3SJohn Marino cp = strchr (name, '/');
2070*86d7f5d3SJohn Marino if (cp != NULL
2071*86d7f5d3SJohn Marino && strlen (arg) == cp - name
2072*86d7f5d3SJohn Marino && strncmp (arg, name, cp - name) == 0)
2073*86d7f5d3SJohn Marino {
2074*86d7f5d3SJohn Marino if (!(timefield = strchr (cp + 1, '/')) || *++timefield == '\0')
2075*86d7f5d3SJohn Marino {
2076*86d7f5d3SJohn Marino /* We didn't find the record separator or it is followed by
2077*86d7f5d3SJohn Marino * the end of the string, so just exit.
2078*86d7f5d3SJohn Marino */
2079*86d7f5d3SJohn Marino if (alloc_pending (80))
2080*86d7f5d3SJohn Marino sprintf (pending_error_text,
2081*86d7f5d3SJohn Marino "E Malformed Entry encountered.");
2082*86d7f5d3SJohn Marino return;
2083*86d7f5d3SJohn Marino }
2084*86d7f5d3SJohn Marino /* If the time field is not currently empty, then one of
2085*86d7f5d3SJohn Marino * serve_modified, serve_is_modified, & serve_unchanged were
2086*86d7f5d3SJohn Marino * already called for this file. We would like to ignore the
2087*86d7f5d3SJohn Marino * reinvocation silently or, better yet, exit with an error
2088*86d7f5d3SJohn Marino * message, but we just avoid the copy-forward and overwrite the
2089*86d7f5d3SJohn Marino * value from the last invocation instead. See the comment below
2090*86d7f5d3SJohn Marino * for more.
2091*86d7f5d3SJohn Marino */
2092*86d7f5d3SJohn Marino if (*timefield == '/')
2093*86d7f5d3SJohn Marino {
2094*86d7f5d3SJohn Marino /* Copy forward one character. Space was allocated for this
2095*86d7f5d3SJohn Marino * already in serve_entry(). */
2096*86d7f5d3SJohn Marino cp = timefield + strlen (timefield);
2097*86d7f5d3SJohn Marino cp[1] = '\0';
2098*86d7f5d3SJohn Marino while (cp > timefield)
2099*86d7f5d3SJohn Marino {
2100*86d7f5d3SJohn Marino *cp = cp[-1];
2101*86d7f5d3SJohn Marino --cp;
2102*86d7f5d3SJohn Marino }
2103*86d7f5d3SJohn Marino
2104*86d7f5d3SJohn Marino /* *timefield == '/'; */
2105*86d7f5d3SJohn Marino }
2106*86d7f5d3SJohn Marino if (*timefield != '+')
2107*86d7f5d3SJohn Marino {
2108*86d7f5d3SJohn Marino /* '+' is a conflict marker and we don't want to mess with it
2109*86d7f5d3SJohn Marino * until Version_TS catches it.
2110*86d7f5d3SJohn Marino */
2111*86d7f5d3SJohn Marino if (timefield[1] != '/')
2112*86d7f5d3SJohn Marino {
2113*86d7f5d3SJohn Marino /* Obliterate anything else in TIMEFIELD. This is again to
2114*86d7f5d3SJohn Marino * support the broken CVSNT clients mentioned below, in
2115*86d7f5d3SJohn Marino * conjunction with strict timestamp string boundry
2116*86d7f5d3SJohn Marino * checking in time_stamp_server() from vers_ts.c &
2117*86d7f5d3SJohn Marino * file_has_conflict() from subr.c, since the broken
2118*86d7f5d3SJohn Marino * clients used to send malformed timestamp fields in the
2119*86d7f5d3SJohn Marino * Entry request that they then depended on the subsequent
2120*86d7f5d3SJohn Marino * Unchanged request to overwrite.
2121*86d7f5d3SJohn Marino */
2122*86d7f5d3SJohn Marino char *d = timefield + 1;
2123*86d7f5d3SJohn Marino if ((cp = strchr (d, '/')))
2124*86d7f5d3SJohn Marino {
2125*86d7f5d3SJohn Marino while (*cp)
2126*86d7f5d3SJohn Marino {
2127*86d7f5d3SJohn Marino *d++ = *cp++;
2128*86d7f5d3SJohn Marino }
2129*86d7f5d3SJohn Marino *d = '\0';
2130*86d7f5d3SJohn Marino }
2131*86d7f5d3SJohn Marino }
2132*86d7f5d3SJohn Marino /* If *TIMEFIELD wasn't '/', we assume that it was because of
2133*86d7f5d3SJohn Marino * multiple calls to Is-modified & Unchanged by the client and
2134*86d7f5d3SJohn Marino * just overwrite the value from the last call. Technically,
2135*86d7f5d3SJohn Marino * we should probably either ignore calls after the first or
2136*86d7f5d3SJohn Marino * send the client an error, since the client/server protocol
2137*86d7f5d3SJohn Marino * specification specifies that only one call to either
2138*86d7f5d3SJohn Marino * Is-Modified or Unchanged is allowed, but broken versions of
2139*86d7f5d3SJohn Marino * CVSNT (at least 2.0.34 - 2.0.41, reported fixed in 2.0.41a)
2140*86d7f5d3SJohn Marino * and the WinCVS & TortoiseCVS clients which depend on those
2141*86d7f5d3SJohn Marino * broken versions of CVSNT (WinCVS 1.3 & at least one
2142*86d7f5d3SJohn Marino * TortoiseCVS release) rely on this behavior.
2143*86d7f5d3SJohn Marino */
2144*86d7f5d3SJohn Marino *timefield = '=';
2145*86d7f5d3SJohn Marino }
2146*86d7f5d3SJohn Marino break;
2147*86d7f5d3SJohn Marino }
2148*86d7f5d3SJohn Marino }
2149*86d7f5d3SJohn Marino }
2150*86d7f5d3SJohn Marino
2151*86d7f5d3SJohn Marino
2152*86d7f5d3SJohn Marino
2153*86d7f5d3SJohn Marino static void
serve_entry(char * arg)2154*86d7f5d3SJohn Marino serve_entry (char *arg)
2155*86d7f5d3SJohn Marino {
2156*86d7f5d3SJohn Marino struct an_entry *p;
2157*86d7f5d3SJohn Marino char *cp;
2158*86d7f5d3SJohn Marino int i = 0;
2159*86d7f5d3SJohn Marino
2160*86d7f5d3SJohn Marino if (error_pending()
2161*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
2162*86d7f5d3SJohn Marino || proxy_log
2163*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
2164*86d7f5d3SJohn Marino ) return;
2165*86d7f5d3SJohn Marino
2166*86d7f5d3SJohn Marino /* Verify that the entry is well-formed. This can avoid problems later.
2167*86d7f5d3SJohn Marino * At the moment we only check that the Entry contains five slashes in
2168*86d7f5d3SJohn Marino * approximately the correct locations since some of the code makes
2169*86d7f5d3SJohn Marino * assumptions about this.
2170*86d7f5d3SJohn Marino */
2171*86d7f5d3SJohn Marino cp = arg;
2172*86d7f5d3SJohn Marino if (*cp == 'D') cp++;
2173*86d7f5d3SJohn Marino while (i++ < 5)
2174*86d7f5d3SJohn Marino {
2175*86d7f5d3SJohn Marino if (!cp || *cp != '/')
2176*86d7f5d3SJohn Marino {
2177*86d7f5d3SJohn Marino if (alloc_pending (80))
2178*86d7f5d3SJohn Marino sprintf (pending_error_text,
2179*86d7f5d3SJohn Marino "E protocol error: Malformed Entry");
2180*86d7f5d3SJohn Marino return;
2181*86d7f5d3SJohn Marino }
2182*86d7f5d3SJohn Marino cp = strchr (cp + 1, '/');
2183*86d7f5d3SJohn Marino }
2184*86d7f5d3SJohn Marino
2185*86d7f5d3SJohn Marino p = xmalloc (sizeof (struct an_entry));
2186*86d7f5d3SJohn Marino if (p == NULL)
2187*86d7f5d3SJohn Marino {
2188*86d7f5d3SJohn Marino pending_error = ENOMEM;
2189*86d7f5d3SJohn Marino return;
2190*86d7f5d3SJohn Marino }
2191*86d7f5d3SJohn Marino /* Leave space for serve_unchanged to write '=' if it wants. */
2192*86d7f5d3SJohn Marino cp = xmalloc (strlen (arg) + 2);
2193*86d7f5d3SJohn Marino if (cp == NULL)
2194*86d7f5d3SJohn Marino {
2195*86d7f5d3SJohn Marino free (p);
2196*86d7f5d3SJohn Marino pending_error = ENOMEM;
2197*86d7f5d3SJohn Marino return;
2198*86d7f5d3SJohn Marino }
2199*86d7f5d3SJohn Marino strcpy (cp, arg);
2200*86d7f5d3SJohn Marino p->next = entries;
2201*86d7f5d3SJohn Marino p->entry = cp;
2202*86d7f5d3SJohn Marino entries = p;
2203*86d7f5d3SJohn Marino }
2204*86d7f5d3SJohn Marino
2205*86d7f5d3SJohn Marino
2206*86d7f5d3SJohn Marino
2207*86d7f5d3SJohn Marino static void
serve_kopt(char * arg)2208*86d7f5d3SJohn Marino serve_kopt (char *arg)
2209*86d7f5d3SJohn Marino {
2210*86d7f5d3SJohn Marino if (error_pending ()
2211*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
2212*86d7f5d3SJohn Marino || proxy_log
2213*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
2214*86d7f5d3SJohn Marino )
2215*86d7f5d3SJohn Marino return;
2216*86d7f5d3SJohn Marino
2217*86d7f5d3SJohn Marino if (kopt != NULL)
2218*86d7f5d3SJohn Marino {
2219*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (arg)))
2220*86d7f5d3SJohn Marino sprintf (pending_error_text,
2221*86d7f5d3SJohn Marino "E protocol error: duplicate Kopt request: %s", arg);
2222*86d7f5d3SJohn Marino return;
2223*86d7f5d3SJohn Marino }
2224*86d7f5d3SJohn Marino
2225*86d7f5d3SJohn Marino /* Do some sanity checks. In particular, that it is not too long.
2226*86d7f5d3SJohn Marino This lets the rest of the code not worry so much about buffer
2227*86d7f5d3SJohn Marino overrun attacks. Probably should call RCS_check_kflag here,
2228*86d7f5d3SJohn Marino but that would mean changing RCS_check_kflag to handle errors
2229*86d7f5d3SJohn Marino other than via exit(), fprintf(), and such. */
2230*86d7f5d3SJohn Marino if (strlen (arg) > 10)
2231*86d7f5d3SJohn Marino {
2232*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (arg)))
2233*86d7f5d3SJohn Marino sprintf (pending_error_text,
2234*86d7f5d3SJohn Marino "E protocol error: invalid Kopt request: %s", arg);
2235*86d7f5d3SJohn Marino return;
2236*86d7f5d3SJohn Marino }
2237*86d7f5d3SJohn Marino
2238*86d7f5d3SJohn Marino kopt = xmalloc (strlen (arg) + 1);
2239*86d7f5d3SJohn Marino if (kopt == NULL)
2240*86d7f5d3SJohn Marino {
2241*86d7f5d3SJohn Marino pending_error = ENOMEM;
2242*86d7f5d3SJohn Marino return;
2243*86d7f5d3SJohn Marino }
2244*86d7f5d3SJohn Marino strcpy (kopt, arg);
2245*86d7f5d3SJohn Marino }
2246*86d7f5d3SJohn Marino
2247*86d7f5d3SJohn Marino
2248*86d7f5d3SJohn Marino
2249*86d7f5d3SJohn Marino static void
serve_checkin_time(char * arg)2250*86d7f5d3SJohn Marino serve_checkin_time (char *arg)
2251*86d7f5d3SJohn Marino {
2252*86d7f5d3SJohn Marino struct timespec t;
2253*86d7f5d3SJohn Marino
2254*86d7f5d3SJohn Marino if (error_pending ()
2255*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
2256*86d7f5d3SJohn Marino || proxy_log
2257*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
2258*86d7f5d3SJohn Marino )
2259*86d7f5d3SJohn Marino return;
2260*86d7f5d3SJohn Marino
2261*86d7f5d3SJohn Marino if (checkin_time_valid)
2262*86d7f5d3SJohn Marino {
2263*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (arg)))
2264*86d7f5d3SJohn Marino sprintf (pending_error_text,
2265*86d7f5d3SJohn Marino "E protocol error: duplicate Checkin-time request: %s",
2266*86d7f5d3SJohn Marino arg);
2267*86d7f5d3SJohn Marino return;
2268*86d7f5d3SJohn Marino }
2269*86d7f5d3SJohn Marino
2270*86d7f5d3SJohn Marino if (!get_date (&t, arg, NULL))
2271*86d7f5d3SJohn Marino {
2272*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (arg)))
2273*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot parse date %s", arg);
2274*86d7f5d3SJohn Marino return;
2275*86d7f5d3SJohn Marino }
2276*86d7f5d3SJohn Marino
2277*86d7f5d3SJohn Marino /* Truncate any nanoseconds returned by get_date(). */
2278*86d7f5d3SJohn Marino checkin_time = t.tv_sec;
2279*86d7f5d3SJohn Marino checkin_time_valid = 1;
2280*86d7f5d3SJohn Marino }
2281*86d7f5d3SJohn Marino
2282*86d7f5d3SJohn Marino
2283*86d7f5d3SJohn Marino
2284*86d7f5d3SJohn Marino static void
server_write_entries(void)2285*86d7f5d3SJohn Marino server_write_entries (void)
2286*86d7f5d3SJohn Marino {
2287*86d7f5d3SJohn Marino FILE *f;
2288*86d7f5d3SJohn Marino struct an_entry *p;
2289*86d7f5d3SJohn Marino struct an_entry *q;
2290*86d7f5d3SJohn Marino
2291*86d7f5d3SJohn Marino if (entries == NULL)
2292*86d7f5d3SJohn Marino return;
2293*86d7f5d3SJohn Marino
2294*86d7f5d3SJohn Marino f = NULL;
2295*86d7f5d3SJohn Marino /* Note that we free all the entries regardless of errors. */
2296*86d7f5d3SJohn Marino if (!error_pending ())
2297*86d7f5d3SJohn Marino {
2298*86d7f5d3SJohn Marino /* We open in append mode because we don't want to clobber an
2299*86d7f5d3SJohn Marino existing Entries file. If we are checking out a module
2300*86d7f5d3SJohn Marino which explicitly lists more than one file in a particular
2301*86d7f5d3SJohn Marino directory, then we will wind up calling
2302*86d7f5d3SJohn Marino server_write_entries for each such file. */
2303*86d7f5d3SJohn Marino f = CVS_FOPEN (CVSADM_ENT, "a");
2304*86d7f5d3SJohn Marino if (f == NULL)
2305*86d7f5d3SJohn Marino {
2306*86d7f5d3SJohn Marino int save_errno = errno;
2307*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (CVSADM_ENT)))
2308*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot open %s", CVSADM_ENT);
2309*86d7f5d3SJohn Marino pending_error = save_errno;
2310*86d7f5d3SJohn Marino }
2311*86d7f5d3SJohn Marino }
2312*86d7f5d3SJohn Marino for (p = entries; p != NULL;)
2313*86d7f5d3SJohn Marino {
2314*86d7f5d3SJohn Marino if (!error_pending ())
2315*86d7f5d3SJohn Marino {
2316*86d7f5d3SJohn Marino if (fprintf (f, "%s\n", p->entry) < 0)
2317*86d7f5d3SJohn Marino {
2318*86d7f5d3SJohn Marino int save_errno = errno;
2319*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen(CVSADM_ENT)))
2320*86d7f5d3SJohn Marino sprintf (pending_error_text,
2321*86d7f5d3SJohn Marino "E cannot write to %s", CVSADM_ENT);
2322*86d7f5d3SJohn Marino pending_error = save_errno;
2323*86d7f5d3SJohn Marino }
2324*86d7f5d3SJohn Marino }
2325*86d7f5d3SJohn Marino free (p->entry);
2326*86d7f5d3SJohn Marino q = p->next;
2327*86d7f5d3SJohn Marino free (p);
2328*86d7f5d3SJohn Marino p = q;
2329*86d7f5d3SJohn Marino }
2330*86d7f5d3SJohn Marino entries = NULL;
2331*86d7f5d3SJohn Marino if (f != NULL && fclose (f) == EOF && !error_pending ())
2332*86d7f5d3SJohn Marino {
2333*86d7f5d3SJohn Marino int save_errno = errno;
2334*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (CVSADM_ENT)))
2335*86d7f5d3SJohn Marino sprintf (pending_error_text, "E cannot close %s", CVSADM_ENT);
2336*86d7f5d3SJohn Marino pending_error = save_errno;
2337*86d7f5d3SJohn Marino }
2338*86d7f5d3SJohn Marino }
2339*86d7f5d3SJohn Marino
2340*86d7f5d3SJohn Marino
2341*86d7f5d3SJohn Marino
2342*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
2343*86d7f5d3SJohn Marino /*
2344*86d7f5d3SJohn Marino * callback proc to run a script when admin finishes.
2345*86d7f5d3SJohn Marino */
2346*86d7f5d3SJohn Marino static int
prepost_proxy_proc(const char * repository,const char * filter,void * closure)2347*86d7f5d3SJohn Marino prepost_proxy_proc (const char *repository, const char *filter, void *closure)
2348*86d7f5d3SJohn Marino {
2349*86d7f5d3SJohn Marino char *cmdline;
2350*86d7f5d3SJohn Marino bool *pre = closure;
2351*86d7f5d3SJohn Marino
2352*86d7f5d3SJohn Marino /* %c = cvs_cmd_name
2353*86d7f5d3SJohn Marino * %p = shortrepos
2354*86d7f5d3SJohn Marino * %r = repository
2355*86d7f5d3SJohn Marino */
2356*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "prepost_proxy_proc (%s, %s, %s)", repository,
2357*86d7f5d3SJohn Marino filter, *pre ? "pre" : "post");
2358*86d7f5d3SJohn Marino
2359*86d7f5d3SJohn Marino /*
2360*86d7f5d3SJohn Marino * Cast any NULL arguments as appropriate pointers as this is an
2361*86d7f5d3SJohn Marino * stdarg function and we need to be certain the caller gets what
2362*86d7f5d3SJohn Marino * is expected.
2363*86d7f5d3SJohn Marino */
2364*86d7f5d3SJohn Marino cmdline = format_cmdline (
2365*86d7f5d3SJohn Marino # ifdef SUPPORT_OLD_INFO_FMT_STRINGS
2366*86d7f5d3SJohn Marino 0, ".",
2367*86d7f5d3SJohn Marino # endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
2368*86d7f5d3SJohn Marino filter,
2369*86d7f5d3SJohn Marino "c", "s", cvs_cmd_name,
2370*86d7f5d3SJohn Marino "R", "s", referrer ? referrer->original : "NONE",
2371*86d7f5d3SJohn Marino "p", "s", ".",
2372*86d7f5d3SJohn Marino "r", "s", current_parsed_root->directory,
2373*86d7f5d3SJohn Marino "P", "s", config->PrimaryServer->original,
2374*86d7f5d3SJohn Marino (char *) NULL);
2375*86d7f5d3SJohn Marino
2376*86d7f5d3SJohn Marino if (!cmdline || !strlen (cmdline))
2377*86d7f5d3SJohn Marino {
2378*86d7f5d3SJohn Marino if (cmdline) free (cmdline);
2379*86d7f5d3SJohn Marino if (*pre)
2380*86d7f5d3SJohn Marino error (0, 0, "preadmin proc resolved to the empty string!");
2381*86d7f5d3SJohn Marino else
2382*86d7f5d3SJohn Marino error (0, 0, "postadmin proc resolved to the empty string!");
2383*86d7f5d3SJohn Marino return 1;
2384*86d7f5d3SJohn Marino }
2385*86d7f5d3SJohn Marino
2386*86d7f5d3SJohn Marino run_setup (cmdline);
2387*86d7f5d3SJohn Marino
2388*86d7f5d3SJohn Marino free (cmdline);
2389*86d7f5d3SJohn Marino
2390*86d7f5d3SJohn Marino /* FIXME - read the comment in verifymsg_proc() about why we use abs()
2391*86d7f5d3SJohn Marino * below() and shouldn't.
2392*86d7f5d3SJohn Marino */
2393*86d7f5d3SJohn Marino return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
2394*86d7f5d3SJohn Marino RUN_NORMAL | RUN_SIGIGNORE));
2395*86d7f5d3SJohn Marino }
2396*86d7f5d3SJohn Marino
2397*86d7f5d3SJohn Marino
2398*86d7f5d3SJohn Marino
2399*86d7f5d3SJohn Marino /* Become a secondary write proxy to a master server.
2400*86d7f5d3SJohn Marino *
2401*86d7f5d3SJohn Marino * This function opens the connection to the primary, dumps the secondary log
2402*86d7f5d3SJohn Marino * to the primary, then reads data from any available connection and writes it
2403*86d7f5d3SJohn Marino * to its partner:
2404*86d7f5d3SJohn Marino *
2405*86d7f5d3SJohn Marino * buf_from_net -> buf_to_primary
2406*86d7f5d3SJohn Marino * buf_from_primary -> buf_to_net
2407*86d7f5d3SJohn Marino *
2408*86d7f5d3SJohn Marino * When all "from" connections have sent EOF and all data has been sent to
2409*86d7f5d3SJohn Marino * "to" connections, this function closes the "to" pipes and returns.
2410*86d7f5d3SJohn Marino */
2411*86d7f5d3SJohn Marino static void
become_proxy(void)2412*86d7f5d3SJohn Marino become_proxy (void)
2413*86d7f5d3SJohn Marino {
2414*86d7f5d3SJohn Marino struct buffer *buf_to_primary;
2415*86d7f5d3SJohn Marino struct buffer *buf_from_primary;
2416*86d7f5d3SJohn Marino
2417*86d7f5d3SJohn Marino /* Close the client log and open it for read. */
2418*86d7f5d3SJohn Marino struct buffer *buf_clientlog = log_buffer_rewind (proxy_log_out);
2419*86d7f5d3SJohn Marino int status, to_primary_fd, from_primary_fd, to_net_fd, from_net_fd;
2420*86d7f5d3SJohn Marino
2421*86d7f5d3SJohn Marino /* Call presecondary script. */
2422*86d7f5d3SJohn Marino bool pre = true;
2423*86d7f5d3SJohn Marino
2424*86d7f5d3SJohn Marino char *data;
2425*86d7f5d3SJohn Marino size_t thispass, got;
2426*86d7f5d3SJohn Marino int s;
2427*86d7f5d3SJohn Marino char *newdata;
2428*86d7f5d3SJohn Marino
2429*86d7f5d3SJohn Marino Parse_Info (CVSROOTADM_PREPROXY, current_parsed_root->directory,
2430*86d7f5d3SJohn Marino prepost_proxy_proc, PIOPT_ALL, &pre);
2431*86d7f5d3SJohn Marino
2432*86d7f5d3SJohn Marino /* Open connection to primary server. */
2433*86d7f5d3SJohn Marino open_connection_to_server (config->PrimaryServer, &buf_to_primary,
2434*86d7f5d3SJohn Marino &buf_from_primary);
2435*86d7f5d3SJohn Marino setup_logfiles ("CVS_SECONDARY_LOG", &buf_to_primary, &buf_from_primary);
2436*86d7f5d3SJohn Marino if ((status = set_nonblock (buf_from_primary)))
2437*86d7f5d3SJohn Marino error (1, status, "failed to set nonblocking io from primary");
2438*86d7f5d3SJohn Marino if ((status = set_nonblock (buf_from_net)))
2439*86d7f5d3SJohn Marino error (1, status, "failed to set nonblocking io from client");
2440*86d7f5d3SJohn Marino if ((status = set_nonblock (buf_to_primary)))
2441*86d7f5d3SJohn Marino error (1, status, "failed to set nonblocking io to primary");
2442*86d7f5d3SJohn Marino if ((status = set_nonblock (buf_to_net)))
2443*86d7f5d3SJohn Marino error (1, status, "failed to set nonblocking io to client");
2444*86d7f5d3SJohn Marino
2445*86d7f5d3SJohn Marino to_primary_fd = buf_get_fd (buf_to_primary);
2446*86d7f5d3SJohn Marino from_primary_fd = buf_get_fd (buf_from_primary);
2447*86d7f5d3SJohn Marino to_net_fd = buf_get_fd (buf_to_net);
2448*86d7f5d3SJohn Marino assert (to_primary_fd >= 0 && from_primary_fd >= 0 && to_net_fd >= 0);
2449*86d7f5d3SJohn Marino
2450*86d7f5d3SJohn Marino /* Close the client log and open it for read. */
2451*86d7f5d3SJohn Marino rewind_buf_from_net ();
2452*86d7f5d3SJohn Marino
2453*86d7f5d3SJohn Marino while (from_primary_fd >= 0 || to_primary_fd >= 0)
2454*86d7f5d3SJohn Marino {
2455*86d7f5d3SJohn Marino fd_set readfds, writefds;
2456*86d7f5d3SJohn Marino int status, numfds = -1;
2457*86d7f5d3SJohn Marino struct timeval *timeout_ptr;
2458*86d7f5d3SJohn Marino struct timeval timeout;
2459*86d7f5d3SJohn Marino size_t toread;
2460*86d7f5d3SJohn Marino
2461*86d7f5d3SJohn Marino FD_ZERO (&readfds);
2462*86d7f5d3SJohn Marino FD_ZERO (&writefds);
2463*86d7f5d3SJohn Marino
2464*86d7f5d3SJohn Marino /* The fd for a multi-source buffer can change with any read. */
2465*86d7f5d3SJohn Marino from_net_fd = buf_from_net ? buf_get_fd (buf_from_net) : -1;
2466*86d7f5d3SJohn Marino
2467*86d7f5d3SJohn Marino if ((buf_from_net && !buf_empty_p (buf_from_net))
2468*86d7f5d3SJohn Marino || (buf_from_primary && !buf_empty_p (buf_from_primary)))
2469*86d7f5d3SJohn Marino {
2470*86d7f5d3SJohn Marino /* There is data pending so don't block if we don't find any new
2471*86d7f5d3SJohn Marino * data on the fds.
2472*86d7f5d3SJohn Marino */
2473*86d7f5d3SJohn Marino timeout.tv_sec = 0;
2474*86d7f5d3SJohn Marino timeout.tv_usec = 0;
2475*86d7f5d3SJohn Marino timeout_ptr = &timeout;
2476*86d7f5d3SJohn Marino }
2477*86d7f5d3SJohn Marino else
2478*86d7f5d3SJohn Marino /* block indefinately */
2479*86d7f5d3SJohn Marino timeout_ptr = NULL;
2480*86d7f5d3SJohn Marino
2481*86d7f5d3SJohn Marino /* Set writefds if data is pending. */
2482*86d7f5d3SJohn Marino if (to_net_fd >= 0 && !buf_empty_p (buf_to_net))
2483*86d7f5d3SJohn Marino {
2484*86d7f5d3SJohn Marino FD_SET (to_net_fd, &writefds);
2485*86d7f5d3SJohn Marino numfds = MAX (numfds, to_net_fd);
2486*86d7f5d3SJohn Marino }
2487*86d7f5d3SJohn Marino if (to_primary_fd >= 0 && !buf_empty_p (buf_to_primary))
2488*86d7f5d3SJohn Marino {
2489*86d7f5d3SJohn Marino FD_SET (to_primary_fd, &writefds);
2490*86d7f5d3SJohn Marino numfds = MAX (numfds, to_primary_fd);
2491*86d7f5d3SJohn Marino }
2492*86d7f5d3SJohn Marino
2493*86d7f5d3SJohn Marino /* Set readfds if descriptors are still open. */
2494*86d7f5d3SJohn Marino if (from_net_fd >= 0)
2495*86d7f5d3SJohn Marino {
2496*86d7f5d3SJohn Marino FD_SET (from_net_fd, &readfds);
2497*86d7f5d3SJohn Marino numfds = MAX (numfds, from_net_fd);
2498*86d7f5d3SJohn Marino }
2499*86d7f5d3SJohn Marino if (from_primary_fd >= 0)
2500*86d7f5d3SJohn Marino {
2501*86d7f5d3SJohn Marino FD_SET (from_primary_fd, &readfds);
2502*86d7f5d3SJohn Marino numfds = MAX (numfds, from_primary_fd);
2503*86d7f5d3SJohn Marino }
2504*86d7f5d3SJohn Marino
2505*86d7f5d3SJohn Marino /* NUMFDS needs to be the highest descriptor + 1 according to the
2506*86d7f5d3SJohn Marino * select spec.
2507*86d7f5d3SJohn Marino */
2508*86d7f5d3SJohn Marino numfds++;
2509*86d7f5d3SJohn Marino
2510*86d7f5d3SJohn Marino do {
2511*86d7f5d3SJohn Marino /* This used to select on exceptions too, but as far
2512*86d7f5d3SJohn Marino as I know there was never any reason to do that and
2513*86d7f5d3SJohn Marino SCO doesn't let you select on exceptions on pipes. */
2514*86d7f5d3SJohn Marino numfds = select (numfds, &readfds, &writefds,
2515*86d7f5d3SJohn Marino NULL, timeout_ptr);
2516*86d7f5d3SJohn Marino if (numfds < 0 && errno != EINTR)
2517*86d7f5d3SJohn Marino {
2518*86d7f5d3SJohn Marino /* Sending an error to the client, possibly in the middle of a
2519*86d7f5d3SJohn Marino * separate protocol message, will likely not mean much to the
2520*86d7f5d3SJohn Marino * client, but it's better than nothing, I guess.
2521*86d7f5d3SJohn Marino */
2522*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E select failed\n");
2523*86d7f5d3SJohn Marino print_error (errno);
2524*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
2525*86d7f5d3SJohn Marino }
2526*86d7f5d3SJohn Marino } while (numfds < 0);
2527*86d7f5d3SJohn Marino
2528*86d7f5d3SJohn Marino if (numfds == 0)
2529*86d7f5d3SJohn Marino {
2530*86d7f5d3SJohn Marino FD_ZERO (&readfds);
2531*86d7f5d3SJohn Marino FD_ZERO (&writefds);
2532*86d7f5d3SJohn Marino }
2533*86d7f5d3SJohn Marino
2534*86d7f5d3SJohn Marino if (to_net_fd >= 0 && FD_ISSET (to_net_fd, &writefds))
2535*86d7f5d3SJohn Marino {
2536*86d7f5d3SJohn Marino /* What should we do with errors? syslog() them? */
2537*86d7f5d3SJohn Marino buf_send_output (buf_to_net);
2538*86d7f5d3SJohn Marino buf_flush (buf_to_net, false);
2539*86d7f5d3SJohn Marino }
2540*86d7f5d3SJohn Marino
2541*86d7f5d3SJohn Marino status = 0;
2542*86d7f5d3SJohn Marino if (from_net_fd >= 0 && (FD_ISSET (from_net_fd, &readfds)))
2543*86d7f5d3SJohn Marino status = buf_input_data (buf_from_net, NULL);
2544*86d7f5d3SJohn Marino
2545*86d7f5d3SJohn Marino if (buf_from_net && !buf_empty_p (buf_from_net))
2546*86d7f5d3SJohn Marino {
2547*86d7f5d3SJohn Marino if (buf_to_primary)
2548*86d7f5d3SJohn Marino buf_append_buffer (buf_to_primary, buf_from_net);
2549*86d7f5d3SJohn Marino else
2550*86d7f5d3SJohn Marino /* (Sys?)log this? */;
2551*86d7f5d3SJohn Marino
2552*86d7f5d3SJohn Marino }
2553*86d7f5d3SJohn Marino
2554*86d7f5d3SJohn Marino if (status == -1 /* EOF */)
2555*86d7f5d3SJohn Marino {
2556*86d7f5d3SJohn Marino SIG_beginCrSect();
2557*86d7f5d3SJohn Marino /* Need only to shut this down and set to NULL, really, in
2558*86d7f5d3SJohn Marino * crit sec, to ensure no double-dispose and to make sure
2559*86d7f5d3SJohn Marino * network pipes are closed as properly as possible, but I
2560*86d7f5d3SJohn Marino * don't see much optimization potential in saving values and
2561*86d7f5d3SJohn Marino * postponing the free.
2562*86d7f5d3SJohn Marino */
2563*86d7f5d3SJohn Marino buf_shutdown (buf_from_net);
2564*86d7f5d3SJohn Marino buf_free (buf_from_net);
2565*86d7f5d3SJohn Marino buf_from_net = NULL;
2566*86d7f5d3SJohn Marino /* So buf_to_primary will be closed at the end of this loop. */
2567*86d7f5d3SJohn Marino from_net_fd = -1;
2568*86d7f5d3SJohn Marino SIG_endCrSect();
2569*86d7f5d3SJohn Marino }
2570*86d7f5d3SJohn Marino else if (status > 0 /* ERRNO */)
2571*86d7f5d3SJohn Marino {
2572*86d7f5d3SJohn Marino buf_output0 (buf_to_net,
2573*86d7f5d3SJohn Marino "E buf_input_data failed reading from client\n");
2574*86d7f5d3SJohn Marino print_error (status);
2575*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
2576*86d7f5d3SJohn Marino }
2577*86d7f5d3SJohn Marino
2578*86d7f5d3SJohn Marino if (to_primary_fd >= 0 && FD_ISSET (to_primary_fd, &writefds))
2579*86d7f5d3SJohn Marino {
2580*86d7f5d3SJohn Marino /* What should we do with errors? syslog() them? */
2581*86d7f5d3SJohn Marino buf_send_output (buf_to_primary);
2582*86d7f5d3SJohn Marino buf_flush (buf_to_primary, false);
2583*86d7f5d3SJohn Marino }
2584*86d7f5d3SJohn Marino
2585*86d7f5d3SJohn Marino status = 0;
2586*86d7f5d3SJohn Marino if (from_primary_fd >= 0 && FD_ISSET (from_primary_fd, &readfds))
2587*86d7f5d3SJohn Marino status = buf_input_data (buf_from_primary, &toread);
2588*86d7f5d3SJohn Marino
2589*86d7f5d3SJohn Marino /* Avoid resending data from the server which we already sent to the
2590*86d7f5d3SJohn Marino * client. Otherwise clients get really confused.
2591*86d7f5d3SJohn Marino */
2592*86d7f5d3SJohn Marino if (buf_clientlog
2593*86d7f5d3SJohn Marino && buf_from_primary && !buf_empty_p (buf_from_primary))
2594*86d7f5d3SJohn Marino {
2595*86d7f5d3SJohn Marino /* Dispose of data we already sent to the client. */
2596*86d7f5d3SJohn Marino while (buf_clientlog && toread > 0)
2597*86d7f5d3SJohn Marino {
2598*86d7f5d3SJohn Marino s = buf_read_data (buf_clientlog, toread, &data, &got);
2599*86d7f5d3SJohn Marino if (s == -2)
2600*86d7f5d3SJohn Marino error (1, ENOMEM, "Failed to read data.");
2601*86d7f5d3SJohn Marino if (s == -1)
2602*86d7f5d3SJohn Marino {
2603*86d7f5d3SJohn Marino buf_shutdown (buf_clientlog);
2604*86d7f5d3SJohn Marino buf_clientlog = NULL;
2605*86d7f5d3SJohn Marino }
2606*86d7f5d3SJohn Marino else if (s)
2607*86d7f5d3SJohn Marino error (1, s, "Error reading writeproxy log.");
2608*86d7f5d3SJohn Marino else
2609*86d7f5d3SJohn Marino {
2610*86d7f5d3SJohn Marino thispass = got;
2611*86d7f5d3SJohn Marino while (thispass > 0)
2612*86d7f5d3SJohn Marino {
2613*86d7f5d3SJohn Marino /* No need to check for errors here since we know we
2614*86d7f5d3SJohn Marino * won't read more than buf_input read into
2615*86d7f5d3SJohn Marino * BUF_FROM_PRIMARY (see how TOREAD is set above).
2616*86d7f5d3SJohn Marino */
2617*86d7f5d3SJohn Marino buf_read_data (buf_from_primary, thispass, &newdata,
2618*86d7f5d3SJohn Marino &got);
2619*86d7f5d3SJohn Marino /* Verify that we are throwing away what we think we
2620*86d7f5d3SJohn Marino * are.
2621*86d7f5d3SJohn Marino *
2622*86d7f5d3SJohn Marino * It is valid to assume that the secondary and primary
2623*86d7f5d3SJohn Marino * are closely enough in sync that this portion of the
2624*86d7f5d3SJohn Marino * communication will be in sync beacuse if they were
2625*86d7f5d3SJohn Marino * not, then the secondary might provide a
2626*86d7f5d3SJohn Marino * valid-request string to the client which contained a
2627*86d7f5d3SJohn Marino * request that the primary didn't support. If the
2628*86d7f5d3SJohn Marino * client later used the request, the primary server
2629*86d7f5d3SJohn Marino * would exit anyhow.
2630*86d7f5d3SJohn Marino *
2631*86d7f5d3SJohn Marino * FIXME?
2632*86d7f5d3SJohn Marino * An alternative approach might be to make sure that
2633*86d7f5d3SJohn Marino * the secondary provides the same string as the
2634*86d7f5d3SJohn Marino * primary regardless, for purposes like pointing a
2635*86d7f5d3SJohn Marino * secondary at an unwitting primary, in which case it
2636*86d7f5d3SJohn Marino * might be useful to have some way to override the
2637*86d7f5d3SJohn Marino * valid-requests string on a secondary, but it seems
2638*86d7f5d3SJohn Marino * much easier to simply sync the versions, at the
2639*86d7f5d3SJohn Marino * moment.
2640*86d7f5d3SJohn Marino */
2641*86d7f5d3SJohn Marino if (memcmp (data, newdata, got))
2642*86d7f5d3SJohn Marino error (1, 0, "Secondary out of sync with primary!");
2643*86d7f5d3SJohn Marino data += got;
2644*86d7f5d3SJohn Marino thispass -= got;
2645*86d7f5d3SJohn Marino }
2646*86d7f5d3SJohn Marino toread -= got;
2647*86d7f5d3SJohn Marino }
2648*86d7f5d3SJohn Marino }
2649*86d7f5d3SJohn Marino }
2650*86d7f5d3SJohn Marino
2651*86d7f5d3SJohn Marino if (buf_from_primary && !buf_empty_p (buf_from_primary))
2652*86d7f5d3SJohn Marino {
2653*86d7f5d3SJohn Marino if (buf_to_net)
2654*86d7f5d3SJohn Marino buf_append_buffer (buf_to_net, buf_from_primary);
2655*86d7f5d3SJohn Marino else
2656*86d7f5d3SJohn Marino /* (Sys?)log this? */;
2657*86d7f5d3SJohn Marino
2658*86d7f5d3SJohn Marino }
2659*86d7f5d3SJohn Marino
2660*86d7f5d3SJohn Marino if (status == -1 /* EOF */)
2661*86d7f5d3SJohn Marino {
2662*86d7f5d3SJohn Marino buf_shutdown (buf_from_primary);
2663*86d7f5d3SJohn Marino buf_from_primary = NULL;
2664*86d7f5d3SJohn Marino from_primary_fd = -1;
2665*86d7f5d3SJohn Marino }
2666*86d7f5d3SJohn Marino else if (status > 0 /* ERRNO */)
2667*86d7f5d3SJohn Marino {
2668*86d7f5d3SJohn Marino buf_output0 (buf_to_net,
2669*86d7f5d3SJohn Marino "E buf_input_data failed reading from primary\n");
2670*86d7f5d3SJohn Marino print_error (status);
2671*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
2672*86d7f5d3SJohn Marino }
2673*86d7f5d3SJohn Marino
2674*86d7f5d3SJohn Marino /* If our "source pipe" is closed and all data has been sent, avoid
2675*86d7f5d3SJohn Marino * selecting it for writability, but don't actually close the buffer in
2676*86d7f5d3SJohn Marino * case other routines want to use it later. The buffer will be closed
2677*86d7f5d3SJohn Marino * in server_cleanup ().
2678*86d7f5d3SJohn Marino */
2679*86d7f5d3SJohn Marino if (from_primary_fd < 0
2680*86d7f5d3SJohn Marino && buf_to_net && buf_empty_p (buf_to_net))
2681*86d7f5d3SJohn Marino to_net_fd = -1;
2682*86d7f5d3SJohn Marino
2683*86d7f5d3SJohn Marino if (buf_to_primary
2684*86d7f5d3SJohn Marino && (/* Assume that there is no further reason to keep the buffer to
2685*86d7f5d3SJohn Marino * the primary open if we can no longer read its responses.
2686*86d7f5d3SJohn Marino */
2687*86d7f5d3SJohn Marino (from_primary_fd < 0 && buf_to_primary)
2688*86d7f5d3SJohn Marino /* Also close buf_to_primary when it becomes impossible to find
2689*86d7f5d3SJohn Marino * more data to send to it. We don't close buf_from_primary
2690*86d7f5d3SJohn Marino * yet since there may be data pending or the primary may react
2691*86d7f5d3SJohn Marino * to the EOF on its input pipe.
2692*86d7f5d3SJohn Marino */
2693*86d7f5d3SJohn Marino || (from_net_fd < 0 && buf_empty_p (buf_to_primary))))
2694*86d7f5d3SJohn Marino {
2695*86d7f5d3SJohn Marino buf_shutdown (buf_to_primary);
2696*86d7f5d3SJohn Marino buf_free (buf_to_primary);
2697*86d7f5d3SJohn Marino buf_to_primary = NULL;
2698*86d7f5d3SJohn Marino
2699*86d7f5d3SJohn Marino /* Setting the fd < 0 with from_primary_fd already < 0 will cause
2700*86d7f5d3SJohn Marino * an escape from this while loop.
2701*86d7f5d3SJohn Marino */
2702*86d7f5d3SJohn Marino to_primary_fd = -1;
2703*86d7f5d3SJohn Marino }
2704*86d7f5d3SJohn Marino }
2705*86d7f5d3SJohn Marino
2706*86d7f5d3SJohn Marino /* Call postsecondary script. */
2707*86d7f5d3SJohn Marino pre = false;
2708*86d7f5d3SJohn Marino Parse_Info (CVSROOTADM_POSTPROXY, current_parsed_root->directory,
2709*86d7f5d3SJohn Marino prepost_proxy_proc, PIOPT_ALL, &pre);
2710*86d7f5d3SJohn Marino }
2711*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
2712*86d7f5d3SJohn Marino
2713*86d7f5d3SJohn Marino
2714*86d7f5d3SJohn Marino
2715*86d7f5d3SJohn Marino struct notify_note {
2716*86d7f5d3SJohn Marino /* Directory in which this notification happens. xmalloc'd*/
2717*86d7f5d3SJohn Marino char *dir;
2718*86d7f5d3SJohn Marino
2719*86d7f5d3SJohn Marino /* xmalloc'd. */
2720*86d7f5d3SJohn Marino char *update_dir;
2721*86d7f5d3SJohn Marino
2722*86d7f5d3SJohn Marino /* xmalloc'd. */
2723*86d7f5d3SJohn Marino char *filename;
2724*86d7f5d3SJohn Marino
2725*86d7f5d3SJohn Marino /* The following three all in one xmalloc'd block, pointed to by TYPE.
2726*86d7f5d3SJohn Marino Each '\0' terminated. */
2727*86d7f5d3SJohn Marino /* "E" or "U". */
2728*86d7f5d3SJohn Marino char *type;
2729*86d7f5d3SJohn Marino /* time+host+dir */
2730*86d7f5d3SJohn Marino char *val;
2731*86d7f5d3SJohn Marino char *watches;
2732*86d7f5d3SJohn Marino
2733*86d7f5d3SJohn Marino struct notify_note *next;
2734*86d7f5d3SJohn Marino };
2735*86d7f5d3SJohn Marino
2736*86d7f5d3SJohn Marino static struct notify_note *notify_list;
2737*86d7f5d3SJohn Marino /* Used while building list, to point to the last node that already exists. */
2738*86d7f5d3SJohn Marino static struct notify_note *last_node;
2739*86d7f5d3SJohn Marino
2740*86d7f5d3SJohn Marino static void
serve_notify(char * arg)2741*86d7f5d3SJohn Marino serve_notify (char *arg)
2742*86d7f5d3SJohn Marino {
2743*86d7f5d3SJohn Marino struct notify_note *new = NULL;
2744*86d7f5d3SJohn Marino char *data = NULL;
2745*86d7f5d3SJohn Marino int status;
2746*86d7f5d3SJohn Marino
2747*86d7f5d3SJohn Marino if (error_pending ()) return;
2748*86d7f5d3SJohn Marino
2749*86d7f5d3SJohn Marino if (isProxyServer())
2750*86d7f5d3SJohn Marino {
2751*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
2752*86d7f5d3SJohn Marino if (!proxy_log)
2753*86d7f5d3SJohn Marino {
2754*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
2755*86d7f5d3SJohn Marino if (alloc_pending (160) + strlen (program_name))
2756*86d7f5d3SJohn Marino sprintf (pending_error_text,
2757*86d7f5d3SJohn Marino "E This CVS server does not support disconnected `%s edit'. For now, remove all `%s' files in your workspace and try your command again.",
2758*86d7f5d3SJohn Marino program_name, CVSADM_NOTIFY);
2759*86d7f5d3SJohn Marino return;
2760*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
2761*86d7f5d3SJohn Marino }
2762*86d7f5d3SJohn Marino else
2763*86d7f5d3SJohn Marino {
2764*86d7f5d3SJohn Marino /* This is effectively a write command, so run it on the primary. */
2765*86d7f5d3SJohn Marino become_proxy ();
2766*86d7f5d3SJohn Marino exit (EXIT_SUCCESS);
2767*86d7f5d3SJohn Marino }
2768*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
2769*86d7f5d3SJohn Marino }
2770*86d7f5d3SJohn Marino
2771*86d7f5d3SJohn Marino if (outside_dir (arg))
2772*86d7f5d3SJohn Marino return;
2773*86d7f5d3SJohn Marino
2774*86d7f5d3SJohn Marino if (gDirname == NULL)
2775*86d7f5d3SJohn Marino goto error;
2776*86d7f5d3SJohn Marino
2777*86d7f5d3SJohn Marino new = xmalloc (sizeof (struct notify_note));
2778*86d7f5d3SJohn Marino if (new == NULL)
2779*86d7f5d3SJohn Marino {
2780*86d7f5d3SJohn Marino pending_error = ENOMEM;
2781*86d7f5d3SJohn Marino return;
2782*86d7f5d3SJohn Marino }
2783*86d7f5d3SJohn Marino new->dir = xmalloc (strlen (gDirname) + 1);
2784*86d7f5d3SJohn Marino new->update_dir = xmalloc (strlen (gupdate_dir) + 1);
2785*86d7f5d3SJohn Marino new->filename = xmalloc (strlen (arg) + 1);
2786*86d7f5d3SJohn Marino if (new->dir == NULL || new->update_dir == NULL || new->filename == NULL)
2787*86d7f5d3SJohn Marino {
2788*86d7f5d3SJohn Marino pending_error = ENOMEM;
2789*86d7f5d3SJohn Marino if (new->dir != NULL)
2790*86d7f5d3SJohn Marino free (new->dir);
2791*86d7f5d3SJohn Marino free (new);
2792*86d7f5d3SJohn Marino return;
2793*86d7f5d3SJohn Marino }
2794*86d7f5d3SJohn Marino strcpy (new->dir, gDirname);
2795*86d7f5d3SJohn Marino strcpy (new->update_dir, gupdate_dir);
2796*86d7f5d3SJohn Marino strcpy (new->filename, arg);
2797*86d7f5d3SJohn Marino
2798*86d7f5d3SJohn Marino status = buf_read_line (buf_from_net, &data, NULL);
2799*86d7f5d3SJohn Marino if (status != 0)
2800*86d7f5d3SJohn Marino {
2801*86d7f5d3SJohn Marino if (status == -2)
2802*86d7f5d3SJohn Marino pending_error = ENOMEM;
2803*86d7f5d3SJohn Marino else
2804*86d7f5d3SJohn Marino {
2805*86d7f5d3SJohn Marino pending_error_text = xmalloc (80 + strlen (arg));
2806*86d7f5d3SJohn Marino if (pending_error_text == NULL)
2807*86d7f5d3SJohn Marino pending_error = ENOMEM;
2808*86d7f5d3SJohn Marino else
2809*86d7f5d3SJohn Marino {
2810*86d7f5d3SJohn Marino if (status == -1)
2811*86d7f5d3SJohn Marino sprintf (pending_error_text,
2812*86d7f5d3SJohn Marino "E end of file reading notification for %s", arg);
2813*86d7f5d3SJohn Marino else
2814*86d7f5d3SJohn Marino {
2815*86d7f5d3SJohn Marino sprintf (pending_error_text,
2816*86d7f5d3SJohn Marino "E error reading notification for %s", arg);
2817*86d7f5d3SJohn Marino pending_error = status;
2818*86d7f5d3SJohn Marino }
2819*86d7f5d3SJohn Marino }
2820*86d7f5d3SJohn Marino }
2821*86d7f5d3SJohn Marino free (new->filename);
2822*86d7f5d3SJohn Marino free (new->dir);
2823*86d7f5d3SJohn Marino free (new);
2824*86d7f5d3SJohn Marino }
2825*86d7f5d3SJohn Marino else
2826*86d7f5d3SJohn Marino {
2827*86d7f5d3SJohn Marino char *cp;
2828*86d7f5d3SJohn Marino
2829*86d7f5d3SJohn Marino if (!data[0])
2830*86d7f5d3SJohn Marino goto error;
2831*86d7f5d3SJohn Marino
2832*86d7f5d3SJohn Marino if (strchr (data, '+'))
2833*86d7f5d3SJohn Marino goto error;
2834*86d7f5d3SJohn Marino
2835*86d7f5d3SJohn Marino new->type = data;
2836*86d7f5d3SJohn Marino if (data[1] != '\t')
2837*86d7f5d3SJohn Marino goto error;
2838*86d7f5d3SJohn Marino data[1] = '\0';
2839*86d7f5d3SJohn Marino cp = data + 2;
2840*86d7f5d3SJohn Marino new->val = cp;
2841*86d7f5d3SJohn Marino cp = strchr (cp, '\t');
2842*86d7f5d3SJohn Marino if (cp == NULL)
2843*86d7f5d3SJohn Marino goto error;
2844*86d7f5d3SJohn Marino *cp++ = '+';
2845*86d7f5d3SJohn Marino cp = strchr (cp, '\t');
2846*86d7f5d3SJohn Marino if (cp == NULL)
2847*86d7f5d3SJohn Marino goto error;
2848*86d7f5d3SJohn Marino *cp++ = '+';
2849*86d7f5d3SJohn Marino cp = strchr (cp, '\t');
2850*86d7f5d3SJohn Marino if (cp == NULL)
2851*86d7f5d3SJohn Marino goto error;
2852*86d7f5d3SJohn Marino *cp++ = '\0';
2853*86d7f5d3SJohn Marino new->watches = cp;
2854*86d7f5d3SJohn Marino /* If there is another tab, ignore everything after it,
2855*86d7f5d3SJohn Marino for future expansion. */
2856*86d7f5d3SJohn Marino cp = strchr (cp, '\t');
2857*86d7f5d3SJohn Marino if (cp != NULL)
2858*86d7f5d3SJohn Marino *cp = '\0';
2859*86d7f5d3SJohn Marino
2860*86d7f5d3SJohn Marino new->next = NULL;
2861*86d7f5d3SJohn Marino
2862*86d7f5d3SJohn Marino if (last_node == NULL)
2863*86d7f5d3SJohn Marino notify_list = new;
2864*86d7f5d3SJohn Marino else
2865*86d7f5d3SJohn Marino last_node->next = new;
2866*86d7f5d3SJohn Marino last_node = new;
2867*86d7f5d3SJohn Marino }
2868*86d7f5d3SJohn Marino return;
2869*86d7f5d3SJohn Marino error:
2870*86d7f5d3SJohn Marino pending_error = 0;
2871*86d7f5d3SJohn Marino if (alloc_pending (80))
2872*86d7f5d3SJohn Marino strcpy (pending_error_text,
2873*86d7f5d3SJohn Marino "E Protocol error; misformed Notify request");
2874*86d7f5d3SJohn Marino if (data != NULL)
2875*86d7f5d3SJohn Marino free (data);
2876*86d7f5d3SJohn Marino if (new != NULL)
2877*86d7f5d3SJohn Marino {
2878*86d7f5d3SJohn Marino free (new->filename);
2879*86d7f5d3SJohn Marino free (new->update_dir);
2880*86d7f5d3SJohn Marino free (new->dir);
2881*86d7f5d3SJohn Marino free (new);
2882*86d7f5d3SJohn Marino }
2883*86d7f5d3SJohn Marino return;
2884*86d7f5d3SJohn Marino }
2885*86d7f5d3SJohn Marino
2886*86d7f5d3SJohn Marino
2887*86d7f5d3SJohn Marino
2888*86d7f5d3SJohn Marino static void
serve_hostname(char * arg)2889*86d7f5d3SJohn Marino serve_hostname (char *arg)
2890*86d7f5d3SJohn Marino {
2891*86d7f5d3SJohn Marino free (hostname);
2892*86d7f5d3SJohn Marino hostname = xstrdup (arg);
2893*86d7f5d3SJohn Marino return;
2894*86d7f5d3SJohn Marino }
2895*86d7f5d3SJohn Marino
2896*86d7f5d3SJohn Marino
2897*86d7f5d3SJohn Marino
2898*86d7f5d3SJohn Marino static void
serve_localdir(char * arg)2899*86d7f5d3SJohn Marino serve_localdir (char *arg)
2900*86d7f5d3SJohn Marino {
2901*86d7f5d3SJohn Marino if (CurDir) free (CurDir);
2902*86d7f5d3SJohn Marino CurDir = xstrdup (arg);
2903*86d7f5d3SJohn Marino }
2904*86d7f5d3SJohn Marino
2905*86d7f5d3SJohn Marino
2906*86d7f5d3SJohn Marino
2907*86d7f5d3SJohn Marino /* Process all the Notify requests that we have stored up. Returns 0
2908*86d7f5d3SJohn Marino if successful, if not prints error message (via error()) and
2909*86d7f5d3SJohn Marino returns negative value. */
2910*86d7f5d3SJohn Marino static int
server_notify(void)2911*86d7f5d3SJohn Marino server_notify (void)
2912*86d7f5d3SJohn Marino {
2913*86d7f5d3SJohn Marino struct notify_note *p;
2914*86d7f5d3SJohn Marino char *repos;
2915*86d7f5d3SJohn Marino
2916*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "server_notify()");
2917*86d7f5d3SJohn Marino
2918*86d7f5d3SJohn Marino while (notify_list != NULL)
2919*86d7f5d3SJohn Marino {
2920*86d7f5d3SJohn Marino if (CVS_CHDIR (notify_list->dir) < 0)
2921*86d7f5d3SJohn Marino {
2922*86d7f5d3SJohn Marino error (0, errno, "cannot change to %s", notify_list->dir);
2923*86d7f5d3SJohn Marino return -1;
2924*86d7f5d3SJohn Marino }
2925*86d7f5d3SJohn Marino repos = Name_Repository (NULL, NULL);
2926*86d7f5d3SJohn Marino
2927*86d7f5d3SJohn Marino lock_dir_for_write (repos);
2928*86d7f5d3SJohn Marino
2929*86d7f5d3SJohn Marino fileattr_startdir (repos);
2930*86d7f5d3SJohn Marino
2931*86d7f5d3SJohn Marino notify_do (*notify_list->type, notify_list->filename,
2932*86d7f5d3SJohn Marino notify_list->update_dir, getcaller(), notify_list->val,
2933*86d7f5d3SJohn Marino notify_list->watches, repos);
2934*86d7f5d3SJohn Marino
2935*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "Notified ");
2936*86d7f5d3SJohn Marino {
2937*86d7f5d3SJohn Marino char *dir = notify_list->dir + strlen (server_temp_dir) + 1;
2938*86d7f5d3SJohn Marino if (dir[0] == '\0')
2939*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '.');
2940*86d7f5d3SJohn Marino else
2941*86d7f5d3SJohn Marino buf_output0 (buf_to_net, dir);
2942*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '/');
2943*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
2944*86d7f5d3SJohn Marino }
2945*86d7f5d3SJohn Marino buf_output0 (buf_to_net, repos);
2946*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '/');
2947*86d7f5d3SJohn Marino buf_output0 (buf_to_net, notify_list->filename);
2948*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
2949*86d7f5d3SJohn Marino free (repos);
2950*86d7f5d3SJohn Marino
2951*86d7f5d3SJohn Marino p = notify_list->next;
2952*86d7f5d3SJohn Marino free (notify_list->filename);
2953*86d7f5d3SJohn Marino free (notify_list->dir);
2954*86d7f5d3SJohn Marino free (notify_list->type);
2955*86d7f5d3SJohn Marino free (notify_list);
2956*86d7f5d3SJohn Marino notify_list = p;
2957*86d7f5d3SJohn Marino
2958*86d7f5d3SJohn Marino fileattr_write ();
2959*86d7f5d3SJohn Marino fileattr_free ();
2960*86d7f5d3SJohn Marino
2961*86d7f5d3SJohn Marino Lock_Cleanup ();
2962*86d7f5d3SJohn Marino }
2963*86d7f5d3SJohn Marino
2964*86d7f5d3SJohn Marino last_node = NULL;
2965*86d7f5d3SJohn Marino
2966*86d7f5d3SJohn Marino /* The code used to call fflush (stdout) here, but that is no
2967*86d7f5d3SJohn Marino longer necessary. The data is now buffered in buf_to_net,
2968*86d7f5d3SJohn Marino which will be flushed by the caller, do_cvs_command. */
2969*86d7f5d3SJohn Marino
2970*86d7f5d3SJohn Marino return 0;
2971*86d7f5d3SJohn Marino }
2972*86d7f5d3SJohn Marino
2973*86d7f5d3SJohn Marino
2974*86d7f5d3SJohn Marino
2975*86d7f5d3SJohn Marino /* This request is processed in all passes since requests which must
2976*86d7f5d3SJohn Marino * sometimes be processed before it is known whether we are running as a
2977*86d7f5d3SJohn Marino * secondary or not, for instance the `expand-modules' request, sometimes use
2978*86d7f5d3SJohn Marino * the `Arguments'.
2979*86d7f5d3SJohn Marino */
2980*86d7f5d3SJohn Marino static void
serve_argument(char * arg)2981*86d7f5d3SJohn Marino serve_argument (char *arg)
2982*86d7f5d3SJohn Marino {
2983*86d7f5d3SJohn Marino char *p;
2984*86d7f5d3SJohn Marino
2985*86d7f5d3SJohn Marino if (error_pending()) return;
2986*86d7f5d3SJohn Marino
2987*86d7f5d3SJohn Marino if (argument_count >= 10000)
2988*86d7f5d3SJohn Marino {
2989*86d7f5d3SJohn Marino if (alloc_pending (80))
2990*86d7f5d3SJohn Marino sprintf (pending_error_text,
2991*86d7f5d3SJohn Marino "E Protocol error: too many arguments");
2992*86d7f5d3SJohn Marino return;
2993*86d7f5d3SJohn Marino }
2994*86d7f5d3SJohn Marino
2995*86d7f5d3SJohn Marino if (argument_vector_size <= argument_count)
2996*86d7f5d3SJohn Marino {
2997*86d7f5d3SJohn Marino argument_vector_size *= 2;
2998*86d7f5d3SJohn Marino argument_vector = xnrealloc (argument_vector,
2999*86d7f5d3SJohn Marino argument_vector_size, sizeof (char *));
3000*86d7f5d3SJohn Marino if (argument_vector == NULL)
3001*86d7f5d3SJohn Marino {
3002*86d7f5d3SJohn Marino pending_error = ENOMEM;
3003*86d7f5d3SJohn Marino return;
3004*86d7f5d3SJohn Marino }
3005*86d7f5d3SJohn Marino }
3006*86d7f5d3SJohn Marino p = xmalloc (strlen (arg) + 1);
3007*86d7f5d3SJohn Marino if (p == NULL)
3008*86d7f5d3SJohn Marino {
3009*86d7f5d3SJohn Marino pending_error = ENOMEM;
3010*86d7f5d3SJohn Marino return;
3011*86d7f5d3SJohn Marino }
3012*86d7f5d3SJohn Marino strcpy (p, arg);
3013*86d7f5d3SJohn Marino argument_vector[argument_count++] = p;
3014*86d7f5d3SJohn Marino }
3015*86d7f5d3SJohn Marino
3016*86d7f5d3SJohn Marino
3017*86d7f5d3SJohn Marino
3018*86d7f5d3SJohn Marino /* For secondary servers, this is handled in all passes, as is the `Argument'
3019*86d7f5d3SJohn Marino * request, and for the same reasons.
3020*86d7f5d3SJohn Marino */
3021*86d7f5d3SJohn Marino static void
serve_argumentx(char * arg)3022*86d7f5d3SJohn Marino serve_argumentx (char *arg)
3023*86d7f5d3SJohn Marino {
3024*86d7f5d3SJohn Marino char *p;
3025*86d7f5d3SJohn Marino
3026*86d7f5d3SJohn Marino if (error_pending()) return;
3027*86d7f5d3SJohn Marino
3028*86d7f5d3SJohn Marino if (argument_count <= 1)
3029*86d7f5d3SJohn Marino {
3030*86d7f5d3SJohn Marino if (alloc_pending (80))
3031*86d7f5d3SJohn Marino sprintf (pending_error_text,
3032*86d7f5d3SJohn Marino "E Protocol error: called argumentx without prior call to argument");
3033*86d7f5d3SJohn Marino return;
3034*86d7f5d3SJohn Marino }
3035*86d7f5d3SJohn Marino
3036*86d7f5d3SJohn Marino p = argument_vector[argument_count - 1];
3037*86d7f5d3SJohn Marino p = xrealloc (p, strlen (p) + 1 + strlen (arg) + 1);
3038*86d7f5d3SJohn Marino if (p == NULL)
3039*86d7f5d3SJohn Marino {
3040*86d7f5d3SJohn Marino pending_error = ENOMEM;
3041*86d7f5d3SJohn Marino return;
3042*86d7f5d3SJohn Marino }
3043*86d7f5d3SJohn Marino strcat (p, "\n");
3044*86d7f5d3SJohn Marino strcat (p, arg);
3045*86d7f5d3SJohn Marino argument_vector[argument_count - 1] = p;
3046*86d7f5d3SJohn Marino }
3047*86d7f5d3SJohn Marino
3048*86d7f5d3SJohn Marino
3049*86d7f5d3SJohn Marino
3050*86d7f5d3SJohn Marino static void
serve_global_option(char * arg)3051*86d7f5d3SJohn Marino serve_global_option (char *arg)
3052*86d7f5d3SJohn Marino {
3053*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
3054*86d7f5d3SJohn Marino /* This can generate error messages and termination before `Root' requests,
3055*86d7f5d3SJohn Marino * so it must be dealt with in the first pass.
3056*86d7f5d3SJohn Marino */
3057*86d7f5d3SJohn Marino if (reprocessing) return;
3058*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
3059*86d7f5d3SJohn Marino
3060*86d7f5d3SJohn Marino if (arg[0] != '-' || arg[1] == '\0' || arg[2] != '\0')
3061*86d7f5d3SJohn Marino {
3062*86d7f5d3SJohn Marino error_return:
3063*86d7f5d3SJohn Marino if (alloc_pending (strlen (arg) + 80))
3064*86d7f5d3SJohn Marino sprintf (pending_error_text,
3065*86d7f5d3SJohn Marino "E Protocol error: bad global option %s",
3066*86d7f5d3SJohn Marino arg);
3067*86d7f5d3SJohn Marino return;
3068*86d7f5d3SJohn Marino }
3069*86d7f5d3SJohn Marino switch (arg[1])
3070*86d7f5d3SJohn Marino {
3071*86d7f5d3SJohn Marino case 'l':
3072*86d7f5d3SJohn Marino error(0, 0, "WARNING: global `-l' option ignored.");
3073*86d7f5d3SJohn Marino break;
3074*86d7f5d3SJohn Marino case 'n':
3075*86d7f5d3SJohn Marino noexec = 1;
3076*86d7f5d3SJohn Marino logoff = 1;
3077*86d7f5d3SJohn Marino break;
3078*86d7f5d3SJohn Marino case 'q':
3079*86d7f5d3SJohn Marino quiet = 1;
3080*86d7f5d3SJohn Marino break;
3081*86d7f5d3SJohn Marino case 'r':
3082*86d7f5d3SJohn Marino cvswrite = 0;
3083*86d7f5d3SJohn Marino break;
3084*86d7f5d3SJohn Marino case 'Q':
3085*86d7f5d3SJohn Marino really_quiet = 1;
3086*86d7f5d3SJohn Marino break;
3087*86d7f5d3SJohn Marino case 't':
3088*86d7f5d3SJohn Marino trace++;
3089*86d7f5d3SJohn Marino break;
3090*86d7f5d3SJohn Marino default:
3091*86d7f5d3SJohn Marino goto error_return;
3092*86d7f5d3SJohn Marino }
3093*86d7f5d3SJohn Marino }
3094*86d7f5d3SJohn Marino
3095*86d7f5d3SJohn Marino
3096*86d7f5d3SJohn Marino
3097*86d7f5d3SJohn Marino /* This needs to be processed before Root requests, so we allow it to be
3098*86d7f5d3SJohn Marino * be processed before knowing whether we are running as a secondary server
3099*86d7f5d3SJohn Marino * to allow `noop' and `Root' requests to generate errors as before.
3100*86d7f5d3SJohn Marino */
3101*86d7f5d3SJohn Marino static void
serve_set(char * arg)3102*86d7f5d3SJohn Marino serve_set (char *arg)
3103*86d7f5d3SJohn Marino {
3104*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
3105*86d7f5d3SJohn Marino if (reprocessing) return;
3106*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
3107*86d7f5d3SJohn Marino
3108*86d7f5d3SJohn Marino /* FIXME: This sends errors immediately (I think); they should be
3109*86d7f5d3SJohn Marino put into pending_error. */
3110*86d7f5d3SJohn Marino variable_set (arg);
3111*86d7f5d3SJohn Marino }
3112*86d7f5d3SJohn Marino
3113*86d7f5d3SJohn Marino # ifdef ENCRYPTION
3114*86d7f5d3SJohn Marino
3115*86d7f5d3SJohn Marino # ifdef HAVE_KERBEROS
3116*86d7f5d3SJohn Marino
3117*86d7f5d3SJohn Marino static void
serve_kerberos_encrypt(char * arg)3118*86d7f5d3SJohn Marino serve_kerberos_encrypt( char *arg )
3119*86d7f5d3SJohn Marino {
3120*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
3121*86d7f5d3SJohn Marino assert (!proxy_log);
3122*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
3123*86d7f5d3SJohn Marino
3124*86d7f5d3SJohn Marino /* All future communication with the client will be encrypted. */
3125*86d7f5d3SJohn Marino
3126*86d7f5d3SJohn Marino buf_to_net = krb_encrypt_buffer_initialize (buf_to_net, 0, sched,
3127*86d7f5d3SJohn Marino kblock,
3128*86d7f5d3SJohn Marino buf_to_net->memory_error);
3129*86d7f5d3SJohn Marino buf_from_net = krb_encrypt_buffer_initialize (buf_from_net, 1, sched,
3130*86d7f5d3SJohn Marino kblock,
3131*86d7f5d3SJohn Marino buf_from_net->memory_error);
3132*86d7f5d3SJohn Marino }
3133*86d7f5d3SJohn Marino
3134*86d7f5d3SJohn Marino # endif /* HAVE_KERBEROS */
3135*86d7f5d3SJohn Marino
3136*86d7f5d3SJohn Marino # ifdef HAVE_GSSAPI
3137*86d7f5d3SJohn Marino
3138*86d7f5d3SJohn Marino static void
serve_gssapi_encrypt(char * arg)3139*86d7f5d3SJohn Marino serve_gssapi_encrypt( char *arg )
3140*86d7f5d3SJohn Marino {
3141*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
3142*86d7f5d3SJohn Marino assert (!proxy_log);
3143*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
3144*86d7f5d3SJohn Marino
3145*86d7f5d3SJohn Marino if (cvs_gssapi_wrapping)
3146*86d7f5d3SJohn Marino {
3147*86d7f5d3SJohn Marino /* We're already using a gssapi_wrap buffer for stream
3148*86d7f5d3SJohn Marino authentication. Flush everything we've output so far, and
3149*86d7f5d3SJohn Marino turn on encryption for future data. On the input side, we
3150*86d7f5d3SJohn Marino should only have unwrapped as far as the Gssapi-encrypt
3151*86d7f5d3SJohn Marino command, so future unwrapping will become encrypted. */
3152*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
3153*86d7f5d3SJohn Marino cvs_gssapi_encrypt = 1;
3154*86d7f5d3SJohn Marino return;
3155*86d7f5d3SJohn Marino }
3156*86d7f5d3SJohn Marino
3157*86d7f5d3SJohn Marino /* All future communication with the client will be encrypted. */
3158*86d7f5d3SJohn Marino
3159*86d7f5d3SJohn Marino cvs_gssapi_encrypt = 1;
3160*86d7f5d3SJohn Marino
3161*86d7f5d3SJohn Marino buf_to_net = cvs_gssapi_wrap_buffer_initialize (buf_to_net, 0,
3162*86d7f5d3SJohn Marino gcontext,
3163*86d7f5d3SJohn Marino buf_to_net->memory_error);
3164*86d7f5d3SJohn Marino buf_from_net = cvs_gssapi_wrap_buffer_initialize (buf_from_net, 1,
3165*86d7f5d3SJohn Marino gcontext,
3166*86d7f5d3SJohn Marino buf_from_net->memory_error);
3167*86d7f5d3SJohn Marino
3168*86d7f5d3SJohn Marino cvs_gssapi_wrapping = 1;
3169*86d7f5d3SJohn Marino }
3170*86d7f5d3SJohn Marino
3171*86d7f5d3SJohn Marino # endif /* HAVE_GSSAPI */
3172*86d7f5d3SJohn Marino
3173*86d7f5d3SJohn Marino # endif /* ENCRYPTION */
3174*86d7f5d3SJohn Marino
3175*86d7f5d3SJohn Marino # ifdef HAVE_GSSAPI
3176*86d7f5d3SJohn Marino
3177*86d7f5d3SJohn Marino static void
serve_gssapi_authenticate(char * arg)3178*86d7f5d3SJohn Marino serve_gssapi_authenticate (char *arg)
3179*86d7f5d3SJohn Marino {
3180*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
3181*86d7f5d3SJohn Marino assert (!proxy_log);
3182*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
3183*86d7f5d3SJohn Marino
3184*86d7f5d3SJohn Marino if (cvs_gssapi_wrapping)
3185*86d7f5d3SJohn Marino {
3186*86d7f5d3SJohn Marino /* We're already using a gssapi_wrap buffer for encryption.
3187*86d7f5d3SJohn Marino That includes authentication, so we don't have to do
3188*86d7f5d3SJohn Marino anything further. */
3189*86d7f5d3SJohn Marino return;
3190*86d7f5d3SJohn Marino }
3191*86d7f5d3SJohn Marino
3192*86d7f5d3SJohn Marino buf_to_net = cvs_gssapi_wrap_buffer_initialize (buf_to_net, 0,
3193*86d7f5d3SJohn Marino gcontext,
3194*86d7f5d3SJohn Marino buf_to_net->memory_error);
3195*86d7f5d3SJohn Marino buf_from_net = cvs_gssapi_wrap_buffer_initialize (buf_from_net, 1,
3196*86d7f5d3SJohn Marino gcontext,
3197*86d7f5d3SJohn Marino buf_from_net->memory_error);
3198*86d7f5d3SJohn Marino
3199*86d7f5d3SJohn Marino cvs_gssapi_wrapping = 1;
3200*86d7f5d3SJohn Marino }
3201*86d7f5d3SJohn Marino
3202*86d7f5d3SJohn Marino # endif /* HAVE_GSSAPI */
3203*86d7f5d3SJohn Marino
3204*86d7f5d3SJohn Marino
3205*86d7f5d3SJohn Marino
3206*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
3207*86d7f5d3SJohn Marino /* The maximum we'll queue to the remote client before blocking. */
3208*86d7f5d3SJohn Marino # ifndef SERVER_HI_WATER
3209*86d7f5d3SJohn Marino # define SERVER_HI_WATER (2 * 1024 * 1024)
3210*86d7f5d3SJohn Marino # endif /* SERVER_HI_WATER */
3211*86d7f5d3SJohn Marino /* When the buffer drops to this, we restart the child */
3212*86d7f5d3SJohn Marino # ifndef SERVER_LO_WATER
3213*86d7f5d3SJohn Marino # define SERVER_LO_WATER (1 * 1024 * 1024)
3214*86d7f5d3SJohn Marino # endif /* SERVER_LO_WATER */
3215*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
3216*86d7f5d3SJohn Marino
3217*86d7f5d3SJohn Marino
3218*86d7f5d3SJohn Marino
3219*86d7f5d3SJohn Marino static void
serve_questionable(char * arg)3220*86d7f5d3SJohn Marino serve_questionable (char *arg)
3221*86d7f5d3SJohn Marino {
3222*86d7f5d3SJohn Marino static int initted;
3223*86d7f5d3SJohn Marino
3224*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
3225*86d7f5d3SJohn Marino if (proxy_log) return;
3226*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
3227*86d7f5d3SJohn Marino
3228*86d7f5d3SJohn Marino if (error_pending ()) return;
3229*86d7f5d3SJohn Marino
3230*86d7f5d3SJohn Marino if (!initted)
3231*86d7f5d3SJohn Marino {
3232*86d7f5d3SJohn Marino /* Pick up ignores from CVSROOTADM_IGNORE, $HOME/.cvsignore on server,
3233*86d7f5d3SJohn Marino and CVSIGNORE on server. */
3234*86d7f5d3SJohn Marino ign_setup ();
3235*86d7f5d3SJohn Marino initted = 1;
3236*86d7f5d3SJohn Marino }
3237*86d7f5d3SJohn Marino
3238*86d7f5d3SJohn Marino if (gDirname == NULL)
3239*86d7f5d3SJohn Marino {
3240*86d7f5d3SJohn Marino if (alloc_pending (80))
3241*86d7f5d3SJohn Marino sprintf (pending_error_text,
3242*86d7f5d3SJohn Marino "E Protocol error: `Directory' missing");
3243*86d7f5d3SJohn Marino return;
3244*86d7f5d3SJohn Marino }
3245*86d7f5d3SJohn Marino
3246*86d7f5d3SJohn Marino if (outside_dir (arg))
3247*86d7f5d3SJohn Marino return;
3248*86d7f5d3SJohn Marino
3249*86d7f5d3SJohn Marino if (!ign_name (arg))
3250*86d7f5d3SJohn Marino {
3251*86d7f5d3SJohn Marino char *update_dir;
3252*86d7f5d3SJohn Marino
3253*86d7f5d3SJohn Marino buf_output (buf_to_net, "M ? ", 4);
3254*86d7f5d3SJohn Marino update_dir = gDirname + strlen (server_temp_dir) + 1;
3255*86d7f5d3SJohn Marino if (!(update_dir[0] == '.' && update_dir[1] == '\0'))
3256*86d7f5d3SJohn Marino {
3257*86d7f5d3SJohn Marino buf_output0 (buf_to_net, update_dir);
3258*86d7f5d3SJohn Marino buf_output (buf_to_net, "/", 1);
3259*86d7f5d3SJohn Marino }
3260*86d7f5d3SJohn Marino buf_output0 (buf_to_net, arg);
3261*86d7f5d3SJohn Marino buf_output (buf_to_net, "\n", 1);
3262*86d7f5d3SJohn Marino }
3263*86d7f5d3SJohn Marino }
3264*86d7f5d3SJohn Marino
3265*86d7f5d3SJohn Marino
3266*86d7f5d3SJohn Marino
3267*86d7f5d3SJohn Marino static struct buffer *protocol = NULL;
3268*86d7f5d3SJohn Marino
3269*86d7f5d3SJohn Marino /* This is the output which we are saving up to send to the server, in the
3270*86d7f5d3SJohn Marino child process. We will push it through, via the `protocol' buffer, when
3271*86d7f5d3SJohn Marino we have a complete line. */
3272*86d7f5d3SJohn Marino static struct buffer *saved_output;
3273*86d7f5d3SJohn Marino
3274*86d7f5d3SJohn Marino /* Likewise, but stuff which will go to stderr. */
3275*86d7f5d3SJohn Marino static struct buffer *saved_outerr;
3276*86d7f5d3SJohn Marino
3277*86d7f5d3SJohn Marino
3278*86d7f5d3SJohn Marino
3279*86d7f5d3SJohn Marino static void
protocol_memory_error(struct buffer * buf)3280*86d7f5d3SJohn Marino protocol_memory_error (struct buffer *buf)
3281*86d7f5d3SJohn Marino {
3282*86d7f5d3SJohn Marino error (1, ENOMEM, "Virtual memory exhausted");
3283*86d7f5d3SJohn Marino }
3284*86d7f5d3SJohn Marino
3285*86d7f5d3SJohn Marino
3286*86d7f5d3SJohn Marino
3287*86d7f5d3SJohn Marino /* If command is valid, return 1.
3288*86d7f5d3SJohn Marino * Else if command is invalid and croak_on_invalid is set, then die.
3289*86d7f5d3SJohn Marino * Else just return 0 to indicate that command is invalid.
3290*86d7f5d3SJohn Marino */
3291*86d7f5d3SJohn Marino static bool
check_command_valid_p(char * cmd_name)3292*86d7f5d3SJohn Marino check_command_valid_p (char *cmd_name)
3293*86d7f5d3SJohn Marino {
3294*86d7f5d3SJohn Marino /* Right now, only pserver notices invalid commands -- namely,
3295*86d7f5d3SJohn Marino * write attempts by a read-only user. Therefore, if CVS_Username
3296*86d7f5d3SJohn Marino * is not set, this just returns 1, because CVS_Username unset
3297*86d7f5d3SJohn Marino * means pserver is not active.
3298*86d7f5d3SJohn Marino */
3299*86d7f5d3SJohn Marino # ifdef AUTH_SERVER_SUPPORT
3300*86d7f5d3SJohn Marino if (CVS_Username == NULL)
3301*86d7f5d3SJohn Marino return true;
3302*86d7f5d3SJohn Marino
3303*86d7f5d3SJohn Marino if (lookup_command_attribute (cmd_name) & CVS_CMD_MODIFIES_REPOSITORY)
3304*86d7f5d3SJohn Marino {
3305*86d7f5d3SJohn Marino /* This command has the potential to modify the repository, so
3306*86d7f5d3SJohn Marino * we check if the user have permission to do that.
3307*86d7f5d3SJohn Marino *
3308*86d7f5d3SJohn Marino * (Only relevant for remote users -- local users can do
3309*86d7f5d3SJohn Marino * whatever normal Unix file permissions allow them to do.)
3310*86d7f5d3SJohn Marino *
3311*86d7f5d3SJohn Marino * The decision method:
3312*86d7f5d3SJohn Marino *
3313*86d7f5d3SJohn Marino * If $CVSROOT/CVSADMROOT_READERS exists and user is listed
3314*86d7f5d3SJohn Marino * in it, then read-only access for user.
3315*86d7f5d3SJohn Marino *
3316*86d7f5d3SJohn Marino * Or if $CVSROOT/CVSADMROOT_WRITERS exists and user NOT
3317*86d7f5d3SJohn Marino * listed in it, then also read-only access for user.
3318*86d7f5d3SJohn Marino *
3319*86d7f5d3SJohn Marino * Else read-write access for user.
3320*86d7f5d3SJohn Marino */
3321*86d7f5d3SJohn Marino
3322*86d7f5d3SJohn Marino char *linebuf = NULL;
3323*86d7f5d3SJohn Marino int num_red = 0;
3324*86d7f5d3SJohn Marino size_t linebuf_len = 0;
3325*86d7f5d3SJohn Marino char *fname;
3326*86d7f5d3SJohn Marino size_t flen;
3327*86d7f5d3SJohn Marino FILE *fp;
3328*86d7f5d3SJohn Marino int found_it = 0;
3329*86d7f5d3SJohn Marino
3330*86d7f5d3SJohn Marino /* else */
3331*86d7f5d3SJohn Marino flen = strlen (current_parsed_root->directory)
3332*86d7f5d3SJohn Marino + strlen (CVSROOTADM)
3333*86d7f5d3SJohn Marino + strlen (CVSROOTADM_READERS)
3334*86d7f5d3SJohn Marino + 3;
3335*86d7f5d3SJohn Marino
3336*86d7f5d3SJohn Marino fname = xmalloc (flen);
3337*86d7f5d3SJohn Marino (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory,
3338*86d7f5d3SJohn Marino CVSROOTADM, CVSROOTADM_READERS);
3339*86d7f5d3SJohn Marino
3340*86d7f5d3SJohn Marino fp = fopen (fname, "r");
3341*86d7f5d3SJohn Marino
3342*86d7f5d3SJohn Marino if (fp == NULL)
3343*86d7f5d3SJohn Marino {
3344*86d7f5d3SJohn Marino if (!existence_error (errno))
3345*86d7f5d3SJohn Marino {
3346*86d7f5d3SJohn Marino /* Need to deny access, so that attackers can't fool
3347*86d7f5d3SJohn Marino us with some sort of denial of service attack. */
3348*86d7f5d3SJohn Marino error (0, errno, "cannot open %s", fname);
3349*86d7f5d3SJohn Marino free (fname);
3350*86d7f5d3SJohn Marino return false;
3351*86d7f5d3SJohn Marino }
3352*86d7f5d3SJohn Marino }
3353*86d7f5d3SJohn Marino else /* successfully opened readers file */
3354*86d7f5d3SJohn Marino {
3355*86d7f5d3SJohn Marino while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0)
3356*86d7f5d3SJohn Marino {
3357*86d7f5d3SJohn Marino /* Hmmm, is it worth importing my own readline
3358*86d7f5d3SJohn Marino library into CVS? It takes care of chopping
3359*86d7f5d3SJohn Marino leading and trailing whitespace, "#" comments, and
3360*86d7f5d3SJohn Marino newlines automatically when so requested. Would
3361*86d7f5d3SJohn Marino save some code here... -kff */
3362*86d7f5d3SJohn Marino
3363*86d7f5d3SJohn Marino /* Chop newline by hand, for strcmp()'s sake. */
3364*86d7f5d3SJohn Marino if (num_red > 0 && linebuf[num_red - 1] == '\n')
3365*86d7f5d3SJohn Marino linebuf[num_red - 1] = '\0';
3366*86d7f5d3SJohn Marino
3367*86d7f5d3SJohn Marino if (strcmp (linebuf, CVS_Username) == 0)
3368*86d7f5d3SJohn Marino goto handle_invalid;
3369*86d7f5d3SJohn Marino }
3370*86d7f5d3SJohn Marino if (num_red < 0 && !feof (fp))
3371*86d7f5d3SJohn Marino error (0, errno, "cannot read %s", fname);
3372*86d7f5d3SJohn Marino
3373*86d7f5d3SJohn Marino /* If not listed specifically as a reader, then this user
3374*86d7f5d3SJohn Marino has write access by default unless writers are also
3375*86d7f5d3SJohn Marino specified in a file . */
3376*86d7f5d3SJohn Marino if (fclose (fp) < 0)
3377*86d7f5d3SJohn Marino error (0, errno, "cannot close %s", fname);
3378*86d7f5d3SJohn Marino }
3379*86d7f5d3SJohn Marino free (fname);
3380*86d7f5d3SJohn Marino
3381*86d7f5d3SJohn Marino /* Now check the writers file. */
3382*86d7f5d3SJohn Marino
3383*86d7f5d3SJohn Marino flen = strlen (current_parsed_root->directory)
3384*86d7f5d3SJohn Marino + strlen (CVSROOTADM)
3385*86d7f5d3SJohn Marino + strlen (CVSROOTADM_WRITERS)
3386*86d7f5d3SJohn Marino + 3;
3387*86d7f5d3SJohn Marino
3388*86d7f5d3SJohn Marino fname = xmalloc (flen);
3389*86d7f5d3SJohn Marino (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory,
3390*86d7f5d3SJohn Marino CVSROOTADM, CVSROOTADM_WRITERS);
3391*86d7f5d3SJohn Marino
3392*86d7f5d3SJohn Marino fp = fopen (fname, "r");
3393*86d7f5d3SJohn Marino
3394*86d7f5d3SJohn Marino if (fp == NULL)
3395*86d7f5d3SJohn Marino {
3396*86d7f5d3SJohn Marino if (linebuf)
3397*86d7f5d3SJohn Marino free (linebuf);
3398*86d7f5d3SJohn Marino if (existence_error (errno))
3399*86d7f5d3SJohn Marino {
3400*86d7f5d3SJohn Marino /* Writers file does not exist, so everyone is a writer,
3401*86d7f5d3SJohn Marino by default. */
3402*86d7f5d3SJohn Marino free (fname);
3403*86d7f5d3SJohn Marino return true;
3404*86d7f5d3SJohn Marino }
3405*86d7f5d3SJohn Marino else
3406*86d7f5d3SJohn Marino {
3407*86d7f5d3SJohn Marino /* Need to deny access, so that attackers can't fool
3408*86d7f5d3SJohn Marino us with some sort of denial of service attack. */
3409*86d7f5d3SJohn Marino error (0, errno, "cannot read %s", fname);
3410*86d7f5d3SJohn Marino free (fname);
3411*86d7f5d3SJohn Marino return false;
3412*86d7f5d3SJohn Marino }
3413*86d7f5d3SJohn Marino }
3414*86d7f5d3SJohn Marino
3415*86d7f5d3SJohn Marino found_it = 0;
3416*86d7f5d3SJohn Marino while ((num_red = getline (&linebuf, &linebuf_len, fp)) >= 0)
3417*86d7f5d3SJohn Marino {
3418*86d7f5d3SJohn Marino /* Chop newline by hand, for strcmp()'s sake. */
3419*86d7f5d3SJohn Marino if (num_red > 0 && linebuf[num_red - 1] == '\n')
3420*86d7f5d3SJohn Marino linebuf[num_red - 1] = '\0';
3421*86d7f5d3SJohn Marino
3422*86d7f5d3SJohn Marino if (strcmp (linebuf, CVS_Username) == 0)
3423*86d7f5d3SJohn Marino {
3424*86d7f5d3SJohn Marino found_it = 1;
3425*86d7f5d3SJohn Marino break;
3426*86d7f5d3SJohn Marino }
3427*86d7f5d3SJohn Marino }
3428*86d7f5d3SJohn Marino if (num_red < 0 && !feof (fp))
3429*86d7f5d3SJohn Marino error (0, errno, "cannot read %s", fname);
3430*86d7f5d3SJohn Marino
3431*86d7f5d3SJohn Marino if (found_it)
3432*86d7f5d3SJohn Marino {
3433*86d7f5d3SJohn Marino if (fclose (fp) < 0)
3434*86d7f5d3SJohn Marino error (0, errno, "cannot close %s", fname);
3435*86d7f5d3SJohn Marino if (linebuf)
3436*86d7f5d3SJohn Marino free (linebuf);
3437*86d7f5d3SJohn Marino free (fname);
3438*86d7f5d3SJohn Marino return true;
3439*86d7f5d3SJohn Marino }
3440*86d7f5d3SJohn Marino else /* writers file exists, but this user not listed in it */
3441*86d7f5d3SJohn Marino {
3442*86d7f5d3SJohn Marino handle_invalid:
3443*86d7f5d3SJohn Marino if (fclose (fp) < 0)
3444*86d7f5d3SJohn Marino error (0, errno, "cannot close %s", fname);
3445*86d7f5d3SJohn Marino if (linebuf)
3446*86d7f5d3SJohn Marino free (linebuf);
3447*86d7f5d3SJohn Marino free (fname);
3448*86d7f5d3SJohn Marino return false;
3449*86d7f5d3SJohn Marino }
3450*86d7f5d3SJohn Marino }
3451*86d7f5d3SJohn Marino # endif /* AUTH_SERVER_SUPPORT */
3452*86d7f5d3SJohn Marino
3453*86d7f5d3SJohn Marino /* If ever reach end of this function, command must be valid. */
3454*86d7f5d3SJohn Marino return true;
3455*86d7f5d3SJohn Marino }
3456*86d7f5d3SJohn Marino
3457*86d7f5d3SJohn Marino
3458*86d7f5d3SJohn Marino
3459*86d7f5d3SJohn Marino /* Execute COMMAND in a subprocess with the approriate funky things done. */
3460*86d7f5d3SJohn Marino
3461*86d7f5d3SJohn Marino static struct fd_set_wrapper { fd_set fds; } command_fds_to_drain;
3462*86d7f5d3SJohn Marino # ifdef SUNOS_KLUDGE
3463*86d7f5d3SJohn Marino static int max_command_fd;
3464*86d7f5d3SJohn Marino # endif
3465*86d7f5d3SJohn Marino
3466*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
3467*86d7f5d3SJohn Marino static int flowcontrol_pipe[2];
3468*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
3469*86d7f5d3SJohn Marino
3470*86d7f5d3SJohn Marino
3471*86d7f5d3SJohn Marino
3472*86d7f5d3SJohn Marino /*
3473*86d7f5d3SJohn Marino * Set buffer FD to non-blocking I/O. Returns 0 for success or errno
3474*86d7f5d3SJohn Marino * code.
3475*86d7f5d3SJohn Marino */
3476*86d7f5d3SJohn Marino int
set_nonblock_fd(int fd)3477*86d7f5d3SJohn Marino set_nonblock_fd (int fd)
3478*86d7f5d3SJohn Marino {
3479*86d7f5d3SJohn Marino # if defined (F_GETFL) && defined (O_NONBLOCK) && defined (F_SETFL)
3480*86d7f5d3SJohn Marino int flags;
3481*86d7f5d3SJohn Marino
3482*86d7f5d3SJohn Marino flags = fcntl (fd, F_GETFL, 0);
3483*86d7f5d3SJohn Marino if (flags < 0)
3484*86d7f5d3SJohn Marino return errno;
3485*86d7f5d3SJohn Marino if (fcntl (fd, F_SETFL, flags | O_NONBLOCK) < 0)
3486*86d7f5d3SJohn Marino return errno;
3487*86d7f5d3SJohn Marino # endif /* F_GETFL && O_NONBLOCK && F_SETFL */
3488*86d7f5d3SJohn Marino return 0;
3489*86d7f5d3SJohn Marino }
3490*86d7f5d3SJohn Marino
3491*86d7f5d3SJohn Marino
3492*86d7f5d3SJohn Marino
3493*86d7f5d3SJohn Marino static void
do_cvs_command(char * cmd_name,int (* command)(int,char **))3494*86d7f5d3SJohn Marino do_cvs_command (char *cmd_name, int (*command) (int, char **))
3495*86d7f5d3SJohn Marino {
3496*86d7f5d3SJohn Marino /*
3497*86d7f5d3SJohn Marino * The following file descriptors are set to -1 if that file is not
3498*86d7f5d3SJohn Marino * currently open.
3499*86d7f5d3SJohn Marino */
3500*86d7f5d3SJohn Marino
3501*86d7f5d3SJohn Marino /* Data on these pipes is a series of '\n'-terminated lines. */
3502*86d7f5d3SJohn Marino int stdout_pipe[2];
3503*86d7f5d3SJohn Marino int stderr_pipe[2];
3504*86d7f5d3SJohn Marino
3505*86d7f5d3SJohn Marino /*
3506*86d7f5d3SJohn Marino * Data on this pipe is a series of counted (see buf_send_counted)
3507*86d7f5d3SJohn Marino * packets. Each packet must be processed atomically (i.e. not
3508*86d7f5d3SJohn Marino * interleaved with data from stdout_pipe or stderr_pipe).
3509*86d7f5d3SJohn Marino */
3510*86d7f5d3SJohn Marino int protocol_pipe[2];
3511*86d7f5d3SJohn Marino
3512*86d7f5d3SJohn Marino int dev_null_fd = -1;
3513*86d7f5d3SJohn Marino
3514*86d7f5d3SJohn Marino int errs;
3515*86d7f5d3SJohn Marino
3516*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "do_cvs_command (%s)", cmd_name);
3517*86d7f5d3SJohn Marino
3518*86d7f5d3SJohn Marino /* Write proxy logging is always terminated when a command is received.
3519*86d7f5d3SJohn Marino * Therefore, we wish to avoid reprocessing the command since that would
3520*86d7f5d3SJohn Marino * cause endless recursion.
3521*86d7f5d3SJohn Marino */
3522*86d7f5d3SJohn Marino if (isProxyServer())
3523*86d7f5d3SJohn Marino {
3524*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
3525*86d7f5d3SJohn Marino if (reprocessing)
3526*86d7f5d3SJohn Marino /* This must be the second time we've reached this point.
3527*86d7f5d3SJohn Marino * Done reprocessing.
3528*86d7f5d3SJohn Marino */
3529*86d7f5d3SJohn Marino reprocessing = false;
3530*86d7f5d3SJohn Marino else
3531*86d7f5d3SJohn Marino {
3532*86d7f5d3SJohn Marino if (lookup_command_attribute (cmd_name)
3533*86d7f5d3SJohn Marino & CVS_CMD_MODIFIES_REPOSITORY)
3534*86d7f5d3SJohn Marino {
3535*86d7f5d3SJohn Marino become_proxy ();
3536*86d7f5d3SJohn Marino exit (EXIT_SUCCESS);
3537*86d7f5d3SJohn Marino }
3538*86d7f5d3SJohn Marino else if (/* serve_co may have called this already and missing logs
3539*86d7f5d3SJohn Marino * should have generated an error in serve_root().
3540*86d7f5d3SJohn Marino */
3541*86d7f5d3SJohn Marino proxy_log)
3542*86d7f5d3SJohn Marino {
3543*86d7f5d3SJohn Marino /* Set up the log for reprocessing. */
3544*86d7f5d3SJohn Marino rewind_buf_from_net ();
3545*86d7f5d3SJohn Marino /* And return to the main loop in server(), where we will now
3546*86d7f5d3SJohn Marino * find the logged secondary data and reread it.
3547*86d7f5d3SJohn Marino */
3548*86d7f5d3SJohn Marino return;
3549*86d7f5d3SJohn Marino }
3550*86d7f5d3SJohn Marino }
3551*86d7f5d3SJohn Marino # else /* !PROXY_SUPPORT */
3552*86d7f5d3SJohn Marino if (lookup_command_attribute (cmd_name)
3553*86d7f5d3SJohn Marino & CVS_CMD_MODIFIES_REPOSITORY
3554*86d7f5d3SJohn Marino && alloc_pending (120))
3555*86d7f5d3SJohn Marino sprintf (pending_error_text,
3556*86d7f5d3SJohn Marino "E You need a CVS client that supports the `Redirect' response for write requests to this server.");
3557*86d7f5d3SJohn Marino return;
3558*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
3559*86d7f5d3SJohn Marino }
3560*86d7f5d3SJohn Marino
3561*86d7f5d3SJohn Marino command_pid = -1;
3562*86d7f5d3SJohn Marino stdout_pipe[0] = -1;
3563*86d7f5d3SJohn Marino stdout_pipe[1] = -1;
3564*86d7f5d3SJohn Marino stderr_pipe[0] = -1;
3565*86d7f5d3SJohn Marino stderr_pipe[1] = -1;
3566*86d7f5d3SJohn Marino protocol_pipe[0] = -1;
3567*86d7f5d3SJohn Marino protocol_pipe[1] = -1;
3568*86d7f5d3SJohn Marino
3569*86d7f5d3SJohn Marino server_write_entries ();
3570*86d7f5d3SJohn Marino
3571*86d7f5d3SJohn Marino if (print_pending_error ())
3572*86d7f5d3SJohn Marino goto free_args_and_return;
3573*86d7f5d3SJohn Marino
3574*86d7f5d3SJohn Marino /* Global `cvs_cmd_name' is probably "server" right now -- only
3575*86d7f5d3SJohn Marino serve_export() sets it to anything else. So we will use local
3576*86d7f5d3SJohn Marino parameter `cmd_name' to determine if this command is valid for
3577*86d7f5d3SJohn Marino this user. */
3578*86d7f5d3SJohn Marino if (!check_command_valid_p (cmd_name))
3579*86d7f5d3SJohn Marino {
3580*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E ");
3581*86d7f5d3SJohn Marino buf_output0 (buf_to_net, program_name);
3582*86d7f5d3SJohn Marino buf_output0 (buf_to_net, " [server aborted]: \"");
3583*86d7f5d3SJohn Marino buf_output0 (buf_to_net, cmd_name);
3584*86d7f5d3SJohn Marino buf_output0 (buf_to_net,
3585*86d7f5d3SJohn Marino "\" requires write access to the repository\n\
3586*86d7f5d3SJohn Marino error \n");
3587*86d7f5d3SJohn Marino goto free_args_and_return;
3588*86d7f5d3SJohn Marino }
3589*86d7f5d3SJohn Marino cvs_cmd_name = cmd_name;
3590*86d7f5d3SJohn Marino
3591*86d7f5d3SJohn Marino (void) server_notify ();
3592*86d7f5d3SJohn Marino
3593*86d7f5d3SJohn Marino /*
3594*86d7f5d3SJohn Marino * We use a child process which actually does the operation. This
3595*86d7f5d3SJohn Marino * is so we can intercept its standard output. Even if all of CVS
3596*86d7f5d3SJohn Marino * were written to go to some special routine instead of writing
3597*86d7f5d3SJohn Marino * to stdout or stderr, we would still need to do the same thing
3598*86d7f5d3SJohn Marino * for the RCS commands.
3599*86d7f5d3SJohn Marino */
3600*86d7f5d3SJohn Marino
3601*86d7f5d3SJohn Marino if (pipe (stdout_pipe) < 0)
3602*86d7f5d3SJohn Marino {
3603*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E pipe failed\n");
3604*86d7f5d3SJohn Marino print_error (errno);
3605*86d7f5d3SJohn Marino goto error_exit;
3606*86d7f5d3SJohn Marino }
3607*86d7f5d3SJohn Marino if (pipe (stderr_pipe) < 0)
3608*86d7f5d3SJohn Marino {
3609*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E pipe failed\n");
3610*86d7f5d3SJohn Marino print_error (errno);
3611*86d7f5d3SJohn Marino goto error_exit;
3612*86d7f5d3SJohn Marino }
3613*86d7f5d3SJohn Marino if (pipe (protocol_pipe) < 0)
3614*86d7f5d3SJohn Marino {
3615*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E pipe failed\n");
3616*86d7f5d3SJohn Marino print_error (errno);
3617*86d7f5d3SJohn Marino goto error_exit;
3618*86d7f5d3SJohn Marino }
3619*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
3620*86d7f5d3SJohn Marino if (pipe (flowcontrol_pipe) < 0)
3621*86d7f5d3SJohn Marino {
3622*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E pipe failed\n");
3623*86d7f5d3SJohn Marino print_error (errno);
3624*86d7f5d3SJohn Marino goto error_exit;
3625*86d7f5d3SJohn Marino }
3626*86d7f5d3SJohn Marino set_nonblock_fd (flowcontrol_pipe[0]);
3627*86d7f5d3SJohn Marino set_nonblock_fd (flowcontrol_pipe[1]);
3628*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
3629*86d7f5d3SJohn Marino
3630*86d7f5d3SJohn Marino dev_null_fd = CVS_OPEN (DEVNULL, O_RDONLY);
3631*86d7f5d3SJohn Marino if (dev_null_fd < 0)
3632*86d7f5d3SJohn Marino {
3633*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E open /dev/null failed\n");
3634*86d7f5d3SJohn Marino print_error (errno);
3635*86d7f5d3SJohn Marino goto error_exit;
3636*86d7f5d3SJohn Marino }
3637*86d7f5d3SJohn Marino
3638*86d7f5d3SJohn Marino /* We shouldn't have any partial lines from cvs_output and
3639*86d7f5d3SJohn Marino cvs_outerr, but we handle them here in case there is a bug. */
3640*86d7f5d3SJohn Marino /* FIXME: appending a newline, rather than using "MT" as we
3641*86d7f5d3SJohn Marino do in the child process, is probably not really a very good
3642*86d7f5d3SJohn Marino way to "handle" them. */
3643*86d7f5d3SJohn Marino if (! buf_empty_p (saved_output))
3644*86d7f5d3SJohn Marino {
3645*86d7f5d3SJohn Marino buf_append_char (saved_output, '\n');
3646*86d7f5d3SJohn Marino buf_copy_lines (buf_to_net, saved_output, 'M');
3647*86d7f5d3SJohn Marino }
3648*86d7f5d3SJohn Marino if (! buf_empty_p (saved_outerr))
3649*86d7f5d3SJohn Marino {
3650*86d7f5d3SJohn Marino buf_append_char (saved_outerr, '\n');
3651*86d7f5d3SJohn Marino buf_copy_lines (buf_to_net, saved_outerr, 'E');
3652*86d7f5d3SJohn Marino }
3653*86d7f5d3SJohn Marino
3654*86d7f5d3SJohn Marino /* Flush out any pending data. */
3655*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
3656*86d7f5d3SJohn Marino
3657*86d7f5d3SJohn Marino /* Don't use vfork; we're not going to exec(). */
3658*86d7f5d3SJohn Marino command_pid = fork ();
3659*86d7f5d3SJohn Marino if (command_pid < 0)
3660*86d7f5d3SJohn Marino {
3661*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E fork failed\n");
3662*86d7f5d3SJohn Marino print_error (errno);
3663*86d7f5d3SJohn Marino goto error_exit;
3664*86d7f5d3SJohn Marino }
3665*86d7f5d3SJohn Marino if (command_pid == 0)
3666*86d7f5d3SJohn Marino {
3667*86d7f5d3SJohn Marino int exitstatus;
3668*86d7f5d3SJohn Marino
3669*86d7f5d3SJohn Marino /* Since we're in the child, and the parent is going to take
3670*86d7f5d3SJohn Marino care of packaging up our error messages, we can clear this
3671*86d7f5d3SJohn Marino flag. */
3672*86d7f5d3SJohn Marino error_use_protocol = 0;
3673*86d7f5d3SJohn Marino
3674*86d7f5d3SJohn Marino protocol = fd_buffer_initialize (protocol_pipe[1], 0, NULL, false,
3675*86d7f5d3SJohn Marino protocol_memory_error);
3676*86d7f5d3SJohn Marino
3677*86d7f5d3SJohn Marino /* At this point we should no longer be using buf_to_net and
3678*86d7f5d3SJohn Marino buf_from_net. Instead, everything should go through
3679*86d7f5d3SJohn Marino protocol. */
3680*86d7f5d3SJohn Marino if (buf_to_net != NULL)
3681*86d7f5d3SJohn Marino {
3682*86d7f5d3SJohn Marino buf_free (buf_to_net);
3683*86d7f5d3SJohn Marino buf_to_net = NULL;
3684*86d7f5d3SJohn Marino }
3685*86d7f5d3SJohn Marino if (buf_from_net != NULL)
3686*86d7f5d3SJohn Marino {
3687*86d7f5d3SJohn Marino buf_free (buf_from_net);
3688*86d7f5d3SJohn Marino buf_from_net = NULL;
3689*86d7f5d3SJohn Marino }
3690*86d7f5d3SJohn Marino
3691*86d7f5d3SJohn Marino /* These were originally set up to use outbuf_memory_error.
3692*86d7f5d3SJohn Marino Since we're now in the child, we should use the simpler
3693*86d7f5d3SJohn Marino protocol_memory_error function. */
3694*86d7f5d3SJohn Marino saved_output->memory_error = protocol_memory_error;
3695*86d7f5d3SJohn Marino saved_outerr->memory_error = protocol_memory_error;
3696*86d7f5d3SJohn Marino
3697*86d7f5d3SJohn Marino if (dup2 (dev_null_fd, STDIN_FILENO) < 0)
3698*86d7f5d3SJohn Marino error (1, errno, "can't set up pipes");
3699*86d7f5d3SJohn Marino if (dup2 (stdout_pipe[1], STDOUT_FILENO) < 0)
3700*86d7f5d3SJohn Marino error (1, errno, "can't set up pipes");
3701*86d7f5d3SJohn Marino if (dup2 (stderr_pipe[1], STDERR_FILENO) < 0)
3702*86d7f5d3SJohn Marino error (1, errno, "can't set up pipes");
3703*86d7f5d3SJohn Marino close (dev_null_fd);
3704*86d7f5d3SJohn Marino close (stdout_pipe[0]);
3705*86d7f5d3SJohn Marino close (stdout_pipe[1]);
3706*86d7f5d3SJohn Marino close (stderr_pipe[0]);
3707*86d7f5d3SJohn Marino close (stderr_pipe[1]);
3708*86d7f5d3SJohn Marino close (protocol_pipe[0]);
3709*86d7f5d3SJohn Marino close_on_exec (protocol_pipe[1]);
3710*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
3711*86d7f5d3SJohn Marino close_on_exec (flowcontrol_pipe[0]);
3712*86d7f5d3SJohn Marino close (flowcontrol_pipe[1]);
3713*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
3714*86d7f5d3SJohn Marino
3715*86d7f5d3SJohn Marino /*
3716*86d7f5d3SJohn Marino * Set this in .bashrc if you want to give yourself time to attach
3717*86d7f5d3SJohn Marino * to the subprocess with a debugger.
3718*86d7f5d3SJohn Marino */
3719*86d7f5d3SJohn Marino if (getenv ("CVS_SERVER_SLEEP"))
3720*86d7f5d3SJohn Marino {
3721*86d7f5d3SJohn Marino int secs = atoi (getenv ("CVS_SERVER_SLEEP"));
3722*86d7f5d3SJohn Marino TRACE (TRACE_DATA, "Sleeping CVS_SERVER_SLEEP (%d) seconds", secs);
3723*86d7f5d3SJohn Marino sleep (secs);
3724*86d7f5d3SJohn Marino }
3725*86d7f5d3SJohn Marino else
3726*86d7f5d3SJohn Marino TRACE (TRACE_DATA, "CVS_SERVER_SLEEP not set.");
3727*86d7f5d3SJohn Marino
3728*86d7f5d3SJohn Marino exitstatus = (*command) (argument_count, argument_vector);
3729*86d7f5d3SJohn Marino
3730*86d7f5d3SJohn Marino /* Output any partial lines. If the client doesn't support
3731*86d7f5d3SJohn Marino "MT", we go ahead and just tack on a newline since the
3732*86d7f5d3SJohn Marino protocol doesn't support anything better. */
3733*86d7f5d3SJohn Marino if (! buf_empty_p (saved_output))
3734*86d7f5d3SJohn Marino {
3735*86d7f5d3SJohn Marino buf_output0 (protocol, supported_response ("MT") ? "MT text " : "M ");
3736*86d7f5d3SJohn Marino buf_append_buffer (protocol, saved_output);
3737*86d7f5d3SJohn Marino buf_output (protocol, "\n", 1);
3738*86d7f5d3SJohn Marino buf_send_counted (protocol);
3739*86d7f5d3SJohn Marino }
3740*86d7f5d3SJohn Marino /* For now we just discard partial lines on stderr. I suspect
3741*86d7f5d3SJohn Marino that CVS can't write such lines unless there is a bug. */
3742*86d7f5d3SJohn Marino
3743*86d7f5d3SJohn Marino buf_free (protocol);
3744*86d7f5d3SJohn Marino
3745*86d7f5d3SJohn Marino /* Close the pipes explicitly in order to send an EOF to the parent,
3746*86d7f5d3SJohn Marino * then wait for the parent to close the flow control pipe. This
3747*86d7f5d3SJohn Marino * avoids a race condition where a child which dumped more than the
3748*86d7f5d3SJohn Marino * high water mark into the pipes could complete its job and exit,
3749*86d7f5d3SJohn Marino * leaving the parent process to attempt to write a stop byte to the
3750*86d7f5d3SJohn Marino * closed flow control pipe, which earned the parent a SIGPIPE, which
3751*86d7f5d3SJohn Marino * it normally only expects on the network pipe and that causes it to
3752*86d7f5d3SJohn Marino * exit with an error message, rather than the SIGCHILD that it knows
3753*86d7f5d3SJohn Marino * how to handle correctly.
3754*86d7f5d3SJohn Marino */
3755*86d7f5d3SJohn Marino /* Let exit() close STDIN - it's from /dev/null anyhow. */
3756*86d7f5d3SJohn Marino fclose (stderr);
3757*86d7f5d3SJohn Marino fclose (stdout);
3758*86d7f5d3SJohn Marino close (protocol_pipe[1]);
3759*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
3760*86d7f5d3SJohn Marino {
3761*86d7f5d3SJohn Marino char junk;
3762*86d7f5d3SJohn Marino ssize_t status;
3763*86d7f5d3SJohn Marino while ((status = read (flowcontrol_pipe[0], &junk, 1)) > 0
3764*86d7f5d3SJohn Marino || (status == -1 && errno == EAGAIN));
3765*86d7f5d3SJohn Marino }
3766*86d7f5d3SJohn Marino /* FIXME: No point in printing an error message with error(),
3767*86d7f5d3SJohn Marino * as STDERR is already closed, but perhaps this could be syslogged?
3768*86d7f5d3SJohn Marino */
3769*86d7f5d3SJohn Marino # endif
3770*86d7f5d3SJohn Marino
3771*86d7f5d3SJohn Marino exit (exitstatus);
3772*86d7f5d3SJohn Marino }
3773*86d7f5d3SJohn Marino
3774*86d7f5d3SJohn Marino /* OK, sit around getting all the input from the child. */
3775*86d7f5d3SJohn Marino {
3776*86d7f5d3SJohn Marino struct buffer *stdoutbuf;
3777*86d7f5d3SJohn Marino struct buffer *stderrbuf;
3778*86d7f5d3SJohn Marino struct buffer *protocol_inbuf;
3779*86d7f5d3SJohn Marino /* Number of file descriptors to check in select (). */
3780*86d7f5d3SJohn Marino int num_to_check;
3781*86d7f5d3SJohn Marino int count_needed = 1;
3782*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
3783*86d7f5d3SJohn Marino int have_flowcontrolled = 0;
3784*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
3785*86d7f5d3SJohn Marino
3786*86d7f5d3SJohn Marino FD_ZERO (&command_fds_to_drain.fds);
3787*86d7f5d3SJohn Marino num_to_check = stdout_pipe[0];
3788*86d7f5d3SJohn Marino FD_SET (stdout_pipe[0], &command_fds_to_drain.fds);
3789*86d7f5d3SJohn Marino num_to_check = MAX (num_to_check, stderr_pipe[0]);
3790*86d7f5d3SJohn Marino FD_SET (stderr_pipe[0], &command_fds_to_drain.fds);
3791*86d7f5d3SJohn Marino num_to_check = MAX (num_to_check, protocol_pipe[0]);
3792*86d7f5d3SJohn Marino FD_SET (protocol_pipe[0], &command_fds_to_drain.fds);
3793*86d7f5d3SJohn Marino num_to_check = MAX (num_to_check, STDOUT_FILENO);
3794*86d7f5d3SJohn Marino # ifdef SUNOS_KLUDGE
3795*86d7f5d3SJohn Marino max_command_fd = num_to_check;
3796*86d7f5d3SJohn Marino # endif
3797*86d7f5d3SJohn Marino /*
3798*86d7f5d3SJohn Marino * File descriptors are numbered from 0, so num_to_check needs to
3799*86d7f5d3SJohn Marino * be one larger than the largest descriptor.
3800*86d7f5d3SJohn Marino */
3801*86d7f5d3SJohn Marino ++num_to_check;
3802*86d7f5d3SJohn Marino if (num_to_check > FD_SETSIZE)
3803*86d7f5d3SJohn Marino {
3804*86d7f5d3SJohn Marino buf_output0 (buf_to_net,
3805*86d7f5d3SJohn Marino "E internal error: FD_SETSIZE not big enough.\n\
3806*86d7f5d3SJohn Marino error \n");
3807*86d7f5d3SJohn Marino goto error_exit;
3808*86d7f5d3SJohn Marino }
3809*86d7f5d3SJohn Marino
3810*86d7f5d3SJohn Marino stdoutbuf = fd_buffer_initialize (stdout_pipe[0], 0, NULL, true,
3811*86d7f5d3SJohn Marino input_memory_error);
3812*86d7f5d3SJohn Marino
3813*86d7f5d3SJohn Marino stderrbuf = fd_buffer_initialize (stderr_pipe[0], 0, NULL, true,
3814*86d7f5d3SJohn Marino input_memory_error);
3815*86d7f5d3SJohn Marino
3816*86d7f5d3SJohn Marino protocol_inbuf = fd_buffer_initialize (protocol_pipe[0], 0, NULL, true,
3817*86d7f5d3SJohn Marino input_memory_error);
3818*86d7f5d3SJohn Marino
3819*86d7f5d3SJohn Marino set_nonblock (buf_to_net);
3820*86d7f5d3SJohn Marino set_nonblock (stdoutbuf);
3821*86d7f5d3SJohn Marino set_nonblock (stderrbuf);
3822*86d7f5d3SJohn Marino set_nonblock (protocol_inbuf);
3823*86d7f5d3SJohn Marino
3824*86d7f5d3SJohn Marino if (close (stdout_pipe[1]) < 0)
3825*86d7f5d3SJohn Marino {
3826*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E close failed\n");
3827*86d7f5d3SJohn Marino print_error (errno);
3828*86d7f5d3SJohn Marino goto error_exit;
3829*86d7f5d3SJohn Marino }
3830*86d7f5d3SJohn Marino stdout_pipe[1] = -1;
3831*86d7f5d3SJohn Marino
3832*86d7f5d3SJohn Marino if (close (stderr_pipe[1]) < 0)
3833*86d7f5d3SJohn Marino {
3834*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E close failed\n");
3835*86d7f5d3SJohn Marino print_error (errno);
3836*86d7f5d3SJohn Marino goto error_exit;
3837*86d7f5d3SJohn Marino }
3838*86d7f5d3SJohn Marino stderr_pipe[1] = -1;
3839*86d7f5d3SJohn Marino
3840*86d7f5d3SJohn Marino if (close (protocol_pipe[1]) < 0)
3841*86d7f5d3SJohn Marino {
3842*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E close failed\n");
3843*86d7f5d3SJohn Marino print_error (errno);
3844*86d7f5d3SJohn Marino goto error_exit;
3845*86d7f5d3SJohn Marino }
3846*86d7f5d3SJohn Marino protocol_pipe[1] = -1;
3847*86d7f5d3SJohn Marino
3848*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
3849*86d7f5d3SJohn Marino if (close (flowcontrol_pipe[0]) < 0)
3850*86d7f5d3SJohn Marino {
3851*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E close failed\n");
3852*86d7f5d3SJohn Marino print_error (errno);
3853*86d7f5d3SJohn Marino goto error_exit;
3854*86d7f5d3SJohn Marino }
3855*86d7f5d3SJohn Marino flowcontrol_pipe[0] = -1;
3856*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
3857*86d7f5d3SJohn Marino
3858*86d7f5d3SJohn Marino if (close (dev_null_fd) < 0)
3859*86d7f5d3SJohn Marino {
3860*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E close failed\n");
3861*86d7f5d3SJohn Marino print_error (errno);
3862*86d7f5d3SJohn Marino goto error_exit;
3863*86d7f5d3SJohn Marino }
3864*86d7f5d3SJohn Marino dev_null_fd = -1;
3865*86d7f5d3SJohn Marino
3866*86d7f5d3SJohn Marino while (stdout_pipe[0] >= 0
3867*86d7f5d3SJohn Marino || stderr_pipe[0] >= 0
3868*86d7f5d3SJohn Marino || protocol_pipe[0] >= 0
3869*86d7f5d3SJohn Marino || count_needed <= 0)
3870*86d7f5d3SJohn Marino {
3871*86d7f5d3SJohn Marino fd_set readfds;
3872*86d7f5d3SJohn Marino fd_set writefds;
3873*86d7f5d3SJohn Marino int numfds;
3874*86d7f5d3SJohn Marino struct timeval *timeout_ptr;
3875*86d7f5d3SJohn Marino struct timeval timeout;
3876*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
3877*86d7f5d3SJohn Marino int bufmemsize;
3878*86d7f5d3SJohn Marino
3879*86d7f5d3SJohn Marino /*
3880*86d7f5d3SJohn Marino * See if we are swamping the remote client and filling our VM.
3881*86d7f5d3SJohn Marino * Tell child to hold off if we do.
3882*86d7f5d3SJohn Marino */
3883*86d7f5d3SJohn Marino bufmemsize = buf_count_mem (buf_to_net);
3884*86d7f5d3SJohn Marino if (!have_flowcontrolled && (bufmemsize > SERVER_HI_WATER))
3885*86d7f5d3SJohn Marino {
3886*86d7f5d3SJohn Marino if (write(flowcontrol_pipe[1], "S", 1) == 1)
3887*86d7f5d3SJohn Marino have_flowcontrolled = 1;
3888*86d7f5d3SJohn Marino }
3889*86d7f5d3SJohn Marino else if (have_flowcontrolled && (bufmemsize < SERVER_LO_WATER))
3890*86d7f5d3SJohn Marino {
3891*86d7f5d3SJohn Marino if (write(flowcontrol_pipe[1], "G", 1) == 1)
3892*86d7f5d3SJohn Marino have_flowcontrolled = 0;
3893*86d7f5d3SJohn Marino }
3894*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
3895*86d7f5d3SJohn Marino
3896*86d7f5d3SJohn Marino FD_ZERO (&readfds);
3897*86d7f5d3SJohn Marino FD_ZERO (&writefds);
3898*86d7f5d3SJohn Marino
3899*86d7f5d3SJohn Marino if (count_needed <= 0)
3900*86d7f5d3SJohn Marino {
3901*86d7f5d3SJohn Marino /* there is data pending which was read from the protocol pipe
3902*86d7f5d3SJohn Marino * so don't block if we don't find any data
3903*86d7f5d3SJohn Marino */
3904*86d7f5d3SJohn Marino timeout.tv_sec = 0;
3905*86d7f5d3SJohn Marino timeout.tv_usec = 0;
3906*86d7f5d3SJohn Marino timeout_ptr = &timeout;
3907*86d7f5d3SJohn Marino }
3908*86d7f5d3SJohn Marino else
3909*86d7f5d3SJohn Marino {
3910*86d7f5d3SJohn Marino /* block indefinately */
3911*86d7f5d3SJohn Marino timeout_ptr = NULL;
3912*86d7f5d3SJohn Marino }
3913*86d7f5d3SJohn Marino
3914*86d7f5d3SJohn Marino if (! buf_empty_p (buf_to_net))
3915*86d7f5d3SJohn Marino FD_SET (STDOUT_FILENO, &writefds);
3916*86d7f5d3SJohn Marino
3917*86d7f5d3SJohn Marino if (stdout_pipe[0] >= 0)
3918*86d7f5d3SJohn Marino {
3919*86d7f5d3SJohn Marino FD_SET (stdout_pipe[0], &readfds);
3920*86d7f5d3SJohn Marino }
3921*86d7f5d3SJohn Marino if (stderr_pipe[0] >= 0)
3922*86d7f5d3SJohn Marino {
3923*86d7f5d3SJohn Marino FD_SET (stderr_pipe[0], &readfds);
3924*86d7f5d3SJohn Marino }
3925*86d7f5d3SJohn Marino if (protocol_pipe[0] >= 0)
3926*86d7f5d3SJohn Marino {
3927*86d7f5d3SJohn Marino FD_SET (protocol_pipe[0], &readfds);
3928*86d7f5d3SJohn Marino }
3929*86d7f5d3SJohn Marino
3930*86d7f5d3SJohn Marino /* This process of selecting on the three pipes means that
3931*86d7f5d3SJohn Marino we might not get output in the same order in which it
3932*86d7f5d3SJohn Marino was written, thus producing the well-known
3933*86d7f5d3SJohn Marino "out-of-order" bug. If the child process uses
3934*86d7f5d3SJohn Marino cvs_output and cvs_outerr, it will send everything on
3935*86d7f5d3SJohn Marino the protocol_pipe and avoid this problem, so the
3936*86d7f5d3SJohn Marino solution is to use cvs_output and cvs_outerr in the
3937*86d7f5d3SJohn Marino child process. */
3938*86d7f5d3SJohn Marino do {
3939*86d7f5d3SJohn Marino /* This used to select on exceptions too, but as far
3940*86d7f5d3SJohn Marino as I know there was never any reason to do that and
3941*86d7f5d3SJohn Marino SCO doesn't let you select on exceptions on pipes. */
3942*86d7f5d3SJohn Marino numfds = select (num_to_check, &readfds, &writefds,
3943*86d7f5d3SJohn Marino NULL, timeout_ptr);
3944*86d7f5d3SJohn Marino if (numfds < 0
3945*86d7f5d3SJohn Marino && errno != EINTR)
3946*86d7f5d3SJohn Marino {
3947*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E select failed\n");
3948*86d7f5d3SJohn Marino print_error (errno);
3949*86d7f5d3SJohn Marino goto error_exit;
3950*86d7f5d3SJohn Marino }
3951*86d7f5d3SJohn Marino } while (numfds < 0);
3952*86d7f5d3SJohn Marino
3953*86d7f5d3SJohn Marino if (numfds == 0)
3954*86d7f5d3SJohn Marino {
3955*86d7f5d3SJohn Marino FD_ZERO (&readfds);
3956*86d7f5d3SJohn Marino FD_ZERO (&writefds);
3957*86d7f5d3SJohn Marino }
3958*86d7f5d3SJohn Marino
3959*86d7f5d3SJohn Marino if (FD_ISSET (STDOUT_FILENO, &writefds))
3960*86d7f5d3SJohn Marino {
3961*86d7f5d3SJohn Marino /* What should we do with errors? syslog() them? */
3962*86d7f5d3SJohn Marino buf_send_output (buf_to_net);
3963*86d7f5d3SJohn Marino }
3964*86d7f5d3SJohn Marino
3965*86d7f5d3SJohn Marino if (protocol_pipe[0] >= 0
3966*86d7f5d3SJohn Marino && (FD_ISSET (protocol_pipe[0], &readfds)))
3967*86d7f5d3SJohn Marino {
3968*86d7f5d3SJohn Marino int status;
3969*86d7f5d3SJohn Marino size_t count_read;
3970*86d7f5d3SJohn Marino
3971*86d7f5d3SJohn Marino status = buf_input_data (protocol_inbuf, &count_read);
3972*86d7f5d3SJohn Marino
3973*86d7f5d3SJohn Marino if (status == -1)
3974*86d7f5d3SJohn Marino {
3975*86d7f5d3SJohn Marino close (protocol_pipe[0]);
3976*86d7f5d3SJohn Marino protocol_pipe[0] = -1;
3977*86d7f5d3SJohn Marino }
3978*86d7f5d3SJohn Marino else if (status > 0)
3979*86d7f5d3SJohn Marino {
3980*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E buf_input_data failed\n");
3981*86d7f5d3SJohn Marino print_error (status);
3982*86d7f5d3SJohn Marino goto error_exit;
3983*86d7f5d3SJohn Marino }
3984*86d7f5d3SJohn Marino
3985*86d7f5d3SJohn Marino /*
3986*86d7f5d3SJohn Marino * We only call buf_copy_counted if we have read
3987*86d7f5d3SJohn Marino * enough bytes to make it worthwhile. This saves us
3988*86d7f5d3SJohn Marino * from continually recounting the amount of data we
3989*86d7f5d3SJohn Marino * have.
3990*86d7f5d3SJohn Marino */
3991*86d7f5d3SJohn Marino count_needed -= count_read;
3992*86d7f5d3SJohn Marino }
3993*86d7f5d3SJohn Marino /* this is still part of the protocol pipe procedure, but it is
3994*86d7f5d3SJohn Marino * outside the above conditional so that unprocessed data can be
3995*86d7f5d3SJohn Marino * left in the buffer and stderr/stdout can be read when a flush
3996*86d7f5d3SJohn Marino * signal is received and control can return here without passing
3997*86d7f5d3SJohn Marino * through the select code and maybe blocking
3998*86d7f5d3SJohn Marino */
3999*86d7f5d3SJohn Marino while (count_needed <= 0)
4000*86d7f5d3SJohn Marino {
4001*86d7f5d3SJohn Marino int special = 0;
4002*86d7f5d3SJohn Marino
4003*86d7f5d3SJohn Marino count_needed = buf_copy_counted (buf_to_net,
4004*86d7f5d3SJohn Marino protocol_inbuf,
4005*86d7f5d3SJohn Marino &special);
4006*86d7f5d3SJohn Marino
4007*86d7f5d3SJohn Marino /* What should we do with errors? syslog() them? */
4008*86d7f5d3SJohn Marino buf_send_output (buf_to_net);
4009*86d7f5d3SJohn Marino
4010*86d7f5d3SJohn Marino /* If SPECIAL got set to <0, it means that the child
4011*86d7f5d3SJohn Marino * wants us to flush the pipe & maybe stderr or stdout.
4012*86d7f5d3SJohn Marino *
4013*86d7f5d3SJohn Marino * After that we break to read stderr & stdout again before
4014*86d7f5d3SJohn Marino * going back to the protocol pipe
4015*86d7f5d3SJohn Marino *
4016*86d7f5d3SJohn Marino * Upon breaking, count_needed = 0, so the next pass will only
4017*86d7f5d3SJohn Marino * perform a non-blocking select before returning here to finish
4018*86d7f5d3SJohn Marino * processing data we already read from the protocol buffer
4019*86d7f5d3SJohn Marino */
4020*86d7f5d3SJohn Marino if (special == -1)
4021*86d7f5d3SJohn Marino {
4022*86d7f5d3SJohn Marino cvs_flushout();
4023*86d7f5d3SJohn Marino break;
4024*86d7f5d3SJohn Marino }
4025*86d7f5d3SJohn Marino if (special == -2)
4026*86d7f5d3SJohn Marino {
4027*86d7f5d3SJohn Marino /* If the client supports the 'F' command, we send it. */
4028*86d7f5d3SJohn Marino if (supported_response ("F"))
4029*86d7f5d3SJohn Marino {
4030*86d7f5d3SJohn Marino buf_append_char (buf_to_net, 'F');
4031*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
4032*86d7f5d3SJohn Marino }
4033*86d7f5d3SJohn Marino cvs_flusherr ();
4034*86d7f5d3SJohn Marino break;
4035*86d7f5d3SJohn Marino }
4036*86d7f5d3SJohn Marino }
4037*86d7f5d3SJohn Marino
4038*86d7f5d3SJohn Marino if (stdout_pipe[0] >= 0
4039*86d7f5d3SJohn Marino && (FD_ISSET (stdout_pipe[0], &readfds)))
4040*86d7f5d3SJohn Marino {
4041*86d7f5d3SJohn Marino int status;
4042*86d7f5d3SJohn Marino
4043*86d7f5d3SJohn Marino status = buf_input_data (stdoutbuf, NULL);
4044*86d7f5d3SJohn Marino
4045*86d7f5d3SJohn Marino buf_copy_lines (buf_to_net, stdoutbuf, 'M');
4046*86d7f5d3SJohn Marino
4047*86d7f5d3SJohn Marino if (status == -1)
4048*86d7f5d3SJohn Marino {
4049*86d7f5d3SJohn Marino close (stdout_pipe[0]);
4050*86d7f5d3SJohn Marino stdout_pipe[0] = -1;
4051*86d7f5d3SJohn Marino }
4052*86d7f5d3SJohn Marino else if (status > 0)
4053*86d7f5d3SJohn Marino {
4054*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E buf_input_data failed\n");
4055*86d7f5d3SJohn Marino print_error (status);
4056*86d7f5d3SJohn Marino goto error_exit;
4057*86d7f5d3SJohn Marino }
4058*86d7f5d3SJohn Marino
4059*86d7f5d3SJohn Marino /* What should we do with errors? syslog() them? */
4060*86d7f5d3SJohn Marino buf_send_output (buf_to_net);
4061*86d7f5d3SJohn Marino }
4062*86d7f5d3SJohn Marino
4063*86d7f5d3SJohn Marino if (stderr_pipe[0] >= 0
4064*86d7f5d3SJohn Marino && (FD_ISSET (stderr_pipe[0], &readfds)))
4065*86d7f5d3SJohn Marino {
4066*86d7f5d3SJohn Marino int status;
4067*86d7f5d3SJohn Marino
4068*86d7f5d3SJohn Marino status = buf_input_data (stderrbuf, NULL);
4069*86d7f5d3SJohn Marino
4070*86d7f5d3SJohn Marino buf_copy_lines (buf_to_net, stderrbuf, 'E');
4071*86d7f5d3SJohn Marino
4072*86d7f5d3SJohn Marino if (status == -1)
4073*86d7f5d3SJohn Marino {
4074*86d7f5d3SJohn Marino close (stderr_pipe[0]);
4075*86d7f5d3SJohn Marino stderr_pipe[0] = -1;
4076*86d7f5d3SJohn Marino }
4077*86d7f5d3SJohn Marino else if (status > 0)
4078*86d7f5d3SJohn Marino {
4079*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E buf_input_data failed\n");
4080*86d7f5d3SJohn Marino print_error (status);
4081*86d7f5d3SJohn Marino goto error_exit;
4082*86d7f5d3SJohn Marino }
4083*86d7f5d3SJohn Marino
4084*86d7f5d3SJohn Marino /* What should we do with errors? syslog() them? */
4085*86d7f5d3SJohn Marino buf_send_output (buf_to_net);
4086*86d7f5d3SJohn Marino }
4087*86d7f5d3SJohn Marino }
4088*86d7f5d3SJohn Marino
4089*86d7f5d3SJohn Marino /*
4090*86d7f5d3SJohn Marino * OK, we've gotten EOF on all the pipes. If there is
4091*86d7f5d3SJohn Marino * anything left on stdoutbuf or stderrbuf (this could only
4092*86d7f5d3SJohn Marino * happen if there was no trailing newline), send it over.
4093*86d7f5d3SJohn Marino */
4094*86d7f5d3SJohn Marino if (! buf_empty_p (stdoutbuf))
4095*86d7f5d3SJohn Marino {
4096*86d7f5d3SJohn Marino buf_append_char (stdoutbuf, '\n');
4097*86d7f5d3SJohn Marino buf_copy_lines (buf_to_net, stdoutbuf, 'M');
4098*86d7f5d3SJohn Marino }
4099*86d7f5d3SJohn Marino if (! buf_empty_p (stderrbuf))
4100*86d7f5d3SJohn Marino {
4101*86d7f5d3SJohn Marino buf_append_char (stderrbuf, '\n');
4102*86d7f5d3SJohn Marino buf_copy_lines (buf_to_net, stderrbuf, 'E');
4103*86d7f5d3SJohn Marino }
4104*86d7f5d3SJohn Marino if (! buf_empty_p (protocol_inbuf))
4105*86d7f5d3SJohn Marino buf_output0 (buf_to_net,
4106*86d7f5d3SJohn Marino "E Protocol error: uncounted data discarded\n");
4107*86d7f5d3SJohn Marino
4108*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
4109*86d7f5d3SJohn Marino close (flowcontrol_pipe[1]);
4110*86d7f5d3SJohn Marino flowcontrol_pipe[1] = -1;
4111*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
4112*86d7f5d3SJohn Marino
4113*86d7f5d3SJohn Marino errs = 0;
4114*86d7f5d3SJohn Marino
4115*86d7f5d3SJohn Marino while (command_pid > 0)
4116*86d7f5d3SJohn Marino {
4117*86d7f5d3SJohn Marino int status;
4118*86d7f5d3SJohn Marino pid_t waited_pid;
4119*86d7f5d3SJohn Marino waited_pid = waitpid (command_pid, &status, 0);
4120*86d7f5d3SJohn Marino if (waited_pid < 0)
4121*86d7f5d3SJohn Marino {
4122*86d7f5d3SJohn Marino /*
4123*86d7f5d3SJohn Marino * Intentionally ignoring EINTR. Other errors
4124*86d7f5d3SJohn Marino * "can't happen".
4125*86d7f5d3SJohn Marino */
4126*86d7f5d3SJohn Marino continue;
4127*86d7f5d3SJohn Marino }
4128*86d7f5d3SJohn Marino
4129*86d7f5d3SJohn Marino if (WIFEXITED (status))
4130*86d7f5d3SJohn Marino errs += WEXITSTATUS (status);
4131*86d7f5d3SJohn Marino else
4132*86d7f5d3SJohn Marino {
4133*86d7f5d3SJohn Marino int sig = WTERMSIG (status);
4134*86d7f5d3SJohn Marino char buf[50];
4135*86d7f5d3SJohn Marino /*
4136*86d7f5d3SJohn Marino * This is really evil, because signals might be numbered
4137*86d7f5d3SJohn Marino * differently on the two systems. We should be using
4138*86d7f5d3SJohn Marino * signal names (either of the "Terminated" or the "SIGTERM"
4139*86d7f5d3SJohn Marino * variety). But cvs doesn't currently use libiberty...we
4140*86d7f5d3SJohn Marino * could roll our own.... FIXME.
4141*86d7f5d3SJohn Marino */
4142*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E Terminated with fatal signal ");
4143*86d7f5d3SJohn Marino sprintf (buf, "%d\n", sig);
4144*86d7f5d3SJohn Marino buf_output0 (buf_to_net, buf);
4145*86d7f5d3SJohn Marino
4146*86d7f5d3SJohn Marino /* Test for a core dump. */
4147*86d7f5d3SJohn Marino if (WCOREDUMP (status))
4148*86d7f5d3SJohn Marino {
4149*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E Core dumped; preserving ");
4150*86d7f5d3SJohn Marino buf_output0 (buf_to_net, orig_server_temp_dir);
4151*86d7f5d3SJohn Marino buf_output0 (buf_to_net, " on server.\n\
4152*86d7f5d3SJohn Marino E CVS locks may need cleaning up.\n");
4153*86d7f5d3SJohn Marino dont_delete_temp = 1;
4154*86d7f5d3SJohn Marino }
4155*86d7f5d3SJohn Marino ++errs;
4156*86d7f5d3SJohn Marino }
4157*86d7f5d3SJohn Marino if (waited_pid == command_pid)
4158*86d7f5d3SJohn Marino command_pid = -1;
4159*86d7f5d3SJohn Marino }
4160*86d7f5d3SJohn Marino
4161*86d7f5d3SJohn Marino /*
4162*86d7f5d3SJohn Marino * OK, we've waited for the child. By now all CVS locks are free
4163*86d7f5d3SJohn Marino * and it's OK to block on the network.
4164*86d7f5d3SJohn Marino */
4165*86d7f5d3SJohn Marino set_block (buf_to_net);
4166*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
4167*86d7f5d3SJohn Marino buf_shutdown (protocol_inbuf);
4168*86d7f5d3SJohn Marino buf_free (protocol_inbuf);
4169*86d7f5d3SJohn Marino protocol_inbuf = NULL;
4170*86d7f5d3SJohn Marino buf_shutdown (stderrbuf);
4171*86d7f5d3SJohn Marino buf_free (stderrbuf);
4172*86d7f5d3SJohn Marino stderrbuf = NULL;
4173*86d7f5d3SJohn Marino buf_shutdown (stdoutbuf);
4174*86d7f5d3SJohn Marino buf_free (stdoutbuf);
4175*86d7f5d3SJohn Marino stdoutbuf = NULL;
4176*86d7f5d3SJohn Marino }
4177*86d7f5d3SJohn Marino
4178*86d7f5d3SJohn Marino if (errs)
4179*86d7f5d3SJohn Marino /* We will have printed an error message already. */
4180*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "error \n");
4181*86d7f5d3SJohn Marino else
4182*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "ok\n");
4183*86d7f5d3SJohn Marino goto free_args_and_return;
4184*86d7f5d3SJohn Marino
4185*86d7f5d3SJohn Marino error_exit:
4186*86d7f5d3SJohn Marino if (command_pid > 0)
4187*86d7f5d3SJohn Marino kill (command_pid, SIGTERM);
4188*86d7f5d3SJohn Marino
4189*86d7f5d3SJohn Marino while (command_pid > 0)
4190*86d7f5d3SJohn Marino {
4191*86d7f5d3SJohn Marino pid_t waited_pid;
4192*86d7f5d3SJohn Marino waited_pid = waitpid (command_pid, NULL, 0);
4193*86d7f5d3SJohn Marino if (waited_pid < 0 && errno == EINTR)
4194*86d7f5d3SJohn Marino continue;
4195*86d7f5d3SJohn Marino if (waited_pid == command_pid)
4196*86d7f5d3SJohn Marino command_pid = -1;
4197*86d7f5d3SJohn Marino }
4198*86d7f5d3SJohn Marino
4199*86d7f5d3SJohn Marino close (dev_null_fd);
4200*86d7f5d3SJohn Marino close (protocol_pipe[0]);
4201*86d7f5d3SJohn Marino close (protocol_pipe[1]);
4202*86d7f5d3SJohn Marino close (stderr_pipe[0]);
4203*86d7f5d3SJohn Marino close (stderr_pipe[1]);
4204*86d7f5d3SJohn Marino close (stdout_pipe[0]);
4205*86d7f5d3SJohn Marino close (stdout_pipe[1]);
4206*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
4207*86d7f5d3SJohn Marino close (flowcontrol_pipe[0]);
4208*86d7f5d3SJohn Marino close (flowcontrol_pipe[1]);
4209*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
4210*86d7f5d3SJohn Marino
4211*86d7f5d3SJohn Marino free_args_and_return:
4212*86d7f5d3SJohn Marino /* Now free the arguments. */
4213*86d7f5d3SJohn Marino {
4214*86d7f5d3SJohn Marino /* argument_vector[0] is a dummy argument, we don't mess with it. */
4215*86d7f5d3SJohn Marino char **cp;
4216*86d7f5d3SJohn Marino for (cp = argument_vector + 1;
4217*86d7f5d3SJohn Marino cp < argument_vector + argument_count;
4218*86d7f5d3SJohn Marino ++cp)
4219*86d7f5d3SJohn Marino free (*cp);
4220*86d7f5d3SJohn Marino
4221*86d7f5d3SJohn Marino argument_count = 1;
4222*86d7f5d3SJohn Marino }
4223*86d7f5d3SJohn Marino
4224*86d7f5d3SJohn Marino /* Flush out any data not yet sent. */
4225*86d7f5d3SJohn Marino set_block (buf_to_net);
4226*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
4227*86d7f5d3SJohn Marino
4228*86d7f5d3SJohn Marino return;
4229*86d7f5d3SJohn Marino }
4230*86d7f5d3SJohn Marino
4231*86d7f5d3SJohn Marino
4232*86d7f5d3SJohn Marino
4233*86d7f5d3SJohn Marino # ifdef SERVER_FLOWCONTROL
4234*86d7f5d3SJohn Marino /*
4235*86d7f5d3SJohn Marino * Called by the child at convenient points in the server's execution for
4236*86d7f5d3SJohn Marino * the server child to block.. ie: when it has no locks active.
4237*86d7f5d3SJohn Marino */
4238*86d7f5d3SJohn Marino void
server_pause_check(void)4239*86d7f5d3SJohn Marino server_pause_check(void)
4240*86d7f5d3SJohn Marino {
4241*86d7f5d3SJohn Marino int paused = 0;
4242*86d7f5d3SJohn Marino char buf[1];
4243*86d7f5d3SJohn Marino
4244*86d7f5d3SJohn Marino while (read (flowcontrol_pipe[0], buf, 1) == 1)
4245*86d7f5d3SJohn Marino {
4246*86d7f5d3SJohn Marino if (*buf == 'S') /* Stop */
4247*86d7f5d3SJohn Marino paused = 1;
4248*86d7f5d3SJohn Marino else if (*buf == 'G') /* Go */
4249*86d7f5d3SJohn Marino paused = 0;
4250*86d7f5d3SJohn Marino else
4251*86d7f5d3SJohn Marino return; /* ??? */
4252*86d7f5d3SJohn Marino }
4253*86d7f5d3SJohn Marino while (paused) {
4254*86d7f5d3SJohn Marino int numfds, numtocheck;
4255*86d7f5d3SJohn Marino fd_set fds;
4256*86d7f5d3SJohn Marino
4257*86d7f5d3SJohn Marino FD_ZERO (&fds);
4258*86d7f5d3SJohn Marino FD_SET (flowcontrol_pipe[0], &fds);
4259*86d7f5d3SJohn Marino numtocheck = flowcontrol_pipe[0] + 1;
4260*86d7f5d3SJohn Marino
4261*86d7f5d3SJohn Marino do {
4262*86d7f5d3SJohn Marino numfds = select (numtocheck, &fds, NULL, NULL, NULL);
4263*86d7f5d3SJohn Marino if (numfds < 0
4264*86d7f5d3SJohn Marino && errno != EINTR)
4265*86d7f5d3SJohn Marino {
4266*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E select failed\n");
4267*86d7f5d3SJohn Marino print_error (errno);
4268*86d7f5d3SJohn Marino return;
4269*86d7f5d3SJohn Marino }
4270*86d7f5d3SJohn Marino } while (numfds < 0);
4271*86d7f5d3SJohn Marino
4272*86d7f5d3SJohn Marino if (FD_ISSET (flowcontrol_pipe[0], &fds))
4273*86d7f5d3SJohn Marino {
4274*86d7f5d3SJohn Marino int got;
4275*86d7f5d3SJohn Marino
4276*86d7f5d3SJohn Marino while ((got = read (flowcontrol_pipe[0], buf, 1)) == 1)
4277*86d7f5d3SJohn Marino {
4278*86d7f5d3SJohn Marino if (*buf == 'S') /* Stop */
4279*86d7f5d3SJohn Marino paused = 1;
4280*86d7f5d3SJohn Marino else if (*buf == 'G') /* Go */
4281*86d7f5d3SJohn Marino paused = 0;
4282*86d7f5d3SJohn Marino else
4283*86d7f5d3SJohn Marino return; /* ??? */
4284*86d7f5d3SJohn Marino }
4285*86d7f5d3SJohn Marino
4286*86d7f5d3SJohn Marino /* This assumes that we are using BSD or POSIX nonblocking
4287*86d7f5d3SJohn Marino I/O. System V nonblocking I/O returns zero if there is
4288*86d7f5d3SJohn Marino nothing to read. */
4289*86d7f5d3SJohn Marino if (got == 0)
4290*86d7f5d3SJohn Marino error (1, 0, "flow control EOF");
4291*86d7f5d3SJohn Marino if (got < 0 && ! blocking_error (errno))
4292*86d7f5d3SJohn Marino {
4293*86d7f5d3SJohn Marino error (1, errno, "flow control read failed");
4294*86d7f5d3SJohn Marino }
4295*86d7f5d3SJohn Marino }
4296*86d7f5d3SJohn Marino }
4297*86d7f5d3SJohn Marino }
4298*86d7f5d3SJohn Marino # endif /* SERVER_FLOWCONTROL */
4299*86d7f5d3SJohn Marino
4300*86d7f5d3SJohn Marino
4301*86d7f5d3SJohn Marino
4302*86d7f5d3SJohn Marino /* This variable commented in server.h. */
4303*86d7f5d3SJohn Marino char *server_dir = NULL;
4304*86d7f5d3SJohn Marino
4305*86d7f5d3SJohn Marino
4306*86d7f5d3SJohn Marino
4307*86d7f5d3SJohn Marino static void
output_dir(const char * update_dir,const char * repository)4308*86d7f5d3SJohn Marino output_dir (const char *update_dir, const char *repository)
4309*86d7f5d3SJohn Marino {
4310*86d7f5d3SJohn Marino /* Set up SHORT_REPOS. */
4311*86d7f5d3SJohn Marino const char *short_repos = Short_Repository (repository);
4312*86d7f5d3SJohn Marino
4313*86d7f5d3SJohn Marino /* Send the update_dir/repos. */
4314*86d7f5d3SJohn Marino if (server_dir != NULL)
4315*86d7f5d3SJohn Marino {
4316*86d7f5d3SJohn Marino buf_output0 (protocol, server_dir);
4317*86d7f5d3SJohn Marino buf_output0 (protocol, "/");
4318*86d7f5d3SJohn Marino }
4319*86d7f5d3SJohn Marino if (update_dir[0] == '\0')
4320*86d7f5d3SJohn Marino buf_output0 (protocol, ".");
4321*86d7f5d3SJohn Marino else
4322*86d7f5d3SJohn Marino buf_output0 (protocol, update_dir);
4323*86d7f5d3SJohn Marino buf_output0 (protocol, "/\n");
4324*86d7f5d3SJohn Marino if (short_repos[0] == '\0')
4325*86d7f5d3SJohn Marino buf_output0 (protocol, ".");
4326*86d7f5d3SJohn Marino else
4327*86d7f5d3SJohn Marino buf_output0 (protocol, short_repos);
4328*86d7f5d3SJohn Marino buf_output0 (protocol, "/");
4329*86d7f5d3SJohn Marino }
4330*86d7f5d3SJohn Marino
4331*86d7f5d3SJohn Marino
4332*86d7f5d3SJohn Marino
4333*86d7f5d3SJohn Marino /*
4334*86d7f5d3SJohn Marino * Entries line that we are squirreling away to send to the client when
4335*86d7f5d3SJohn Marino * we are ready.
4336*86d7f5d3SJohn Marino */
4337*86d7f5d3SJohn Marino static char *entries_line;
4338*86d7f5d3SJohn Marino
4339*86d7f5d3SJohn Marino /*
4340*86d7f5d3SJohn Marino * File which has been Scratch_File'd, we are squirreling away that fact
4341*86d7f5d3SJohn Marino * to inform the client when we are ready.
4342*86d7f5d3SJohn Marino */
4343*86d7f5d3SJohn Marino static char *scratched_file;
4344*86d7f5d3SJohn Marino
4345*86d7f5d3SJohn Marino /*
4346*86d7f5d3SJohn Marino * The scratched_file will need to be removed as well as having its entry
4347*86d7f5d3SJohn Marino * removed.
4348*86d7f5d3SJohn Marino */
4349*86d7f5d3SJohn Marino static int kill_scratched_file;
4350*86d7f5d3SJohn Marino
4351*86d7f5d3SJohn Marino
4352*86d7f5d3SJohn Marino
4353*86d7f5d3SJohn Marino void
server_register(const char * name,const char * version,const char * timestamp,const char * options,const char * tag,const char * date,const char * conflict)4354*86d7f5d3SJohn Marino server_register (const char *name, const char *version, const char *timestamp,
4355*86d7f5d3SJohn Marino const char *options, const char *tag, const char *date,
4356*86d7f5d3SJohn Marino const char *conflict)
4357*86d7f5d3SJohn Marino {
4358*86d7f5d3SJohn Marino int len;
4359*86d7f5d3SJohn Marino
4360*86d7f5d3SJohn Marino if (options == NULL)
4361*86d7f5d3SJohn Marino options = "";
4362*86d7f5d3SJohn Marino
4363*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "server_register(%s, %s, %s, %s, %s, %s, %s)",
4364*86d7f5d3SJohn Marino name, version, timestamp ? timestamp : "", options,
4365*86d7f5d3SJohn Marino tag ? tag : "", date ? date : "",
4366*86d7f5d3SJohn Marino conflict ? conflict : "");
4367*86d7f5d3SJohn Marino
4368*86d7f5d3SJohn Marino if (entries_line != NULL)
4369*86d7f5d3SJohn Marino {
4370*86d7f5d3SJohn Marino /*
4371*86d7f5d3SJohn Marino * If CVS decides to Register it more than once (which happens
4372*86d7f5d3SJohn Marino * on "cvs update foo/foo.c" where foo and foo.c are already
4373*86d7f5d3SJohn Marino * checked out), use the last of the entries lines Register'd.
4374*86d7f5d3SJohn Marino */
4375*86d7f5d3SJohn Marino free (entries_line);
4376*86d7f5d3SJohn Marino }
4377*86d7f5d3SJohn Marino
4378*86d7f5d3SJohn Marino /*
4379*86d7f5d3SJohn Marino * I have reports of Scratch_Entry and Register both happening, in
4380*86d7f5d3SJohn Marino * two different cases. Using the last one which happens is almost
4381*86d7f5d3SJohn Marino * surely correct; I haven't tracked down why they both happen (or
4382*86d7f5d3SJohn Marino * even verified that they are for the same file).
4383*86d7f5d3SJohn Marino */
4384*86d7f5d3SJohn Marino if (scratched_file != NULL)
4385*86d7f5d3SJohn Marino {
4386*86d7f5d3SJohn Marino free (scratched_file);
4387*86d7f5d3SJohn Marino scratched_file = NULL;
4388*86d7f5d3SJohn Marino }
4389*86d7f5d3SJohn Marino
4390*86d7f5d3SJohn Marino len = (strlen (name) + strlen (version) + strlen (options) + 80);
4391*86d7f5d3SJohn Marino if (tag)
4392*86d7f5d3SJohn Marino len += strlen (tag);
4393*86d7f5d3SJohn Marino if (date)
4394*86d7f5d3SJohn Marino len += strlen (date);
4395*86d7f5d3SJohn Marino
4396*86d7f5d3SJohn Marino entries_line = xmalloc (len);
4397*86d7f5d3SJohn Marino sprintf (entries_line, "/%s/%s/", name, version);
4398*86d7f5d3SJohn Marino if (conflict != NULL)
4399*86d7f5d3SJohn Marino {
4400*86d7f5d3SJohn Marino strcat (entries_line, "+=");
4401*86d7f5d3SJohn Marino }
4402*86d7f5d3SJohn Marino strcat (entries_line, "/");
4403*86d7f5d3SJohn Marino strcat (entries_line, options);
4404*86d7f5d3SJohn Marino strcat (entries_line, "/");
4405*86d7f5d3SJohn Marino if (tag != NULL)
4406*86d7f5d3SJohn Marino {
4407*86d7f5d3SJohn Marino strcat (entries_line, "T");
4408*86d7f5d3SJohn Marino strcat (entries_line, tag);
4409*86d7f5d3SJohn Marino }
4410*86d7f5d3SJohn Marino else if (date != NULL)
4411*86d7f5d3SJohn Marino {
4412*86d7f5d3SJohn Marino strcat (entries_line, "D");
4413*86d7f5d3SJohn Marino strcat (entries_line, date);
4414*86d7f5d3SJohn Marino }
4415*86d7f5d3SJohn Marino }
4416*86d7f5d3SJohn Marino
4417*86d7f5d3SJohn Marino
4418*86d7f5d3SJohn Marino
4419*86d7f5d3SJohn Marino void
server_scratch(const char * fname)4420*86d7f5d3SJohn Marino server_scratch (const char *fname)
4421*86d7f5d3SJohn Marino {
4422*86d7f5d3SJohn Marino /*
4423*86d7f5d3SJohn Marino * I have reports of Scratch_Entry and Register both happening, in
4424*86d7f5d3SJohn Marino * two different cases. Using the last one which happens is almost
4425*86d7f5d3SJohn Marino * surely correct; I haven't tracked down why they both happen (or
4426*86d7f5d3SJohn Marino * even verified that they are for the same file).
4427*86d7f5d3SJohn Marino *
4428*86d7f5d3SJohn Marino * Don't know if this is what whoever wrote the above comment was
4429*86d7f5d3SJohn Marino * talking about, but this can happen in the case where a join
4430*86d7f5d3SJohn Marino * removes a file - the call to Register puts the '-vers' into the
4431*86d7f5d3SJohn Marino * Entries file after the file is removed
4432*86d7f5d3SJohn Marino */
4433*86d7f5d3SJohn Marino if (entries_line != NULL)
4434*86d7f5d3SJohn Marino {
4435*86d7f5d3SJohn Marino free (entries_line);
4436*86d7f5d3SJohn Marino entries_line = NULL;
4437*86d7f5d3SJohn Marino }
4438*86d7f5d3SJohn Marino
4439*86d7f5d3SJohn Marino if (scratched_file != NULL)
4440*86d7f5d3SJohn Marino {
4441*86d7f5d3SJohn Marino buf_output0 (protocol,
4442*86d7f5d3SJohn Marino "E CVS server internal error: duplicate Scratch_Entry\n");
4443*86d7f5d3SJohn Marino buf_send_counted (protocol);
4444*86d7f5d3SJohn Marino return;
4445*86d7f5d3SJohn Marino }
4446*86d7f5d3SJohn Marino scratched_file = xstrdup (fname);
4447*86d7f5d3SJohn Marino kill_scratched_file = 1;
4448*86d7f5d3SJohn Marino }
4449*86d7f5d3SJohn Marino
4450*86d7f5d3SJohn Marino
4451*86d7f5d3SJohn Marino
4452*86d7f5d3SJohn Marino void
server_scratch_entry_only(void)4453*86d7f5d3SJohn Marino server_scratch_entry_only (void)
4454*86d7f5d3SJohn Marino {
4455*86d7f5d3SJohn Marino kill_scratched_file = 0;
4456*86d7f5d3SJohn Marino }
4457*86d7f5d3SJohn Marino
4458*86d7f5d3SJohn Marino
4459*86d7f5d3SJohn Marino
4460*86d7f5d3SJohn Marino /* Print a new entries line, from a previous server_register. */
4461*86d7f5d3SJohn Marino static void
new_entries_line(void)4462*86d7f5d3SJohn Marino new_entries_line (void)
4463*86d7f5d3SJohn Marino {
4464*86d7f5d3SJohn Marino if (entries_line)
4465*86d7f5d3SJohn Marino {
4466*86d7f5d3SJohn Marino buf_output0 (protocol, entries_line);
4467*86d7f5d3SJohn Marino buf_output (protocol, "\n", 1);
4468*86d7f5d3SJohn Marino }
4469*86d7f5d3SJohn Marino else
4470*86d7f5d3SJohn Marino /* Return the error message as the Entries line. */
4471*86d7f5d3SJohn Marino buf_output0 (protocol,
4472*86d7f5d3SJohn Marino "CVS server internal error: Register missing\n");
4473*86d7f5d3SJohn Marino free (entries_line);
4474*86d7f5d3SJohn Marino entries_line = NULL;
4475*86d7f5d3SJohn Marino }
4476*86d7f5d3SJohn Marino
4477*86d7f5d3SJohn Marino
4478*86d7f5d3SJohn Marino
4479*86d7f5d3SJohn Marino static void
serve_ci(char * arg)4480*86d7f5d3SJohn Marino serve_ci (char *arg)
4481*86d7f5d3SJohn Marino {
4482*86d7f5d3SJohn Marino do_cvs_command ("commit", commit);
4483*86d7f5d3SJohn Marino }
4484*86d7f5d3SJohn Marino
4485*86d7f5d3SJohn Marino
4486*86d7f5d3SJohn Marino
4487*86d7f5d3SJohn Marino static void
checked_in_response(const char * file,const char * update_dir,const char * repository)4488*86d7f5d3SJohn Marino checked_in_response (const char *file, const char *update_dir,
4489*86d7f5d3SJohn Marino const char *repository)
4490*86d7f5d3SJohn Marino {
4491*86d7f5d3SJohn Marino if (supported_response ("Mode"))
4492*86d7f5d3SJohn Marino {
4493*86d7f5d3SJohn Marino struct stat sb;
4494*86d7f5d3SJohn Marino char *mode_string;
4495*86d7f5d3SJohn Marino
4496*86d7f5d3SJohn Marino if (stat (file, &sb) < 0)
4497*86d7f5d3SJohn Marino {
4498*86d7f5d3SJohn Marino /* Not clear to me why the file would fail to exist, but it
4499*86d7f5d3SJohn Marino was happening somewhere in the testsuite. */
4500*86d7f5d3SJohn Marino if (!existence_error (errno))
4501*86d7f5d3SJohn Marino error (0, errno, "cannot stat %s", file);
4502*86d7f5d3SJohn Marino }
4503*86d7f5d3SJohn Marino else
4504*86d7f5d3SJohn Marino {
4505*86d7f5d3SJohn Marino buf_output0 (protocol, "Mode ");
4506*86d7f5d3SJohn Marino mode_string = mode_to_string (sb.st_mode);
4507*86d7f5d3SJohn Marino buf_output0 (protocol, mode_string);
4508*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
4509*86d7f5d3SJohn Marino free (mode_string);
4510*86d7f5d3SJohn Marino }
4511*86d7f5d3SJohn Marino }
4512*86d7f5d3SJohn Marino
4513*86d7f5d3SJohn Marino buf_output0 (protocol, "Checked-in ");
4514*86d7f5d3SJohn Marino output_dir (update_dir, repository);
4515*86d7f5d3SJohn Marino buf_output0 (protocol, file);
4516*86d7f5d3SJohn Marino buf_output (protocol, "\n", 1);
4517*86d7f5d3SJohn Marino new_entries_line ();
4518*86d7f5d3SJohn Marino }
4519*86d7f5d3SJohn Marino
4520*86d7f5d3SJohn Marino
4521*86d7f5d3SJohn Marino
4522*86d7f5d3SJohn Marino void
server_checked_in(const char * file,const char * update_dir,const char * repository)4523*86d7f5d3SJohn Marino server_checked_in (const char *file, const char *update_dir,
4524*86d7f5d3SJohn Marino const char *repository)
4525*86d7f5d3SJohn Marino {
4526*86d7f5d3SJohn Marino if (noexec)
4527*86d7f5d3SJohn Marino return;
4528*86d7f5d3SJohn Marino if (scratched_file != NULL && entries_line == NULL)
4529*86d7f5d3SJohn Marino {
4530*86d7f5d3SJohn Marino /*
4531*86d7f5d3SJohn Marino * This happens if we are now doing a "cvs remove" after a previous
4532*86d7f5d3SJohn Marino * "cvs add" (without a "cvs ci" in between).
4533*86d7f5d3SJohn Marino */
4534*86d7f5d3SJohn Marino buf_output0 (protocol, "Remove-entry ");
4535*86d7f5d3SJohn Marino output_dir (update_dir, repository);
4536*86d7f5d3SJohn Marino buf_output0 (protocol, file);
4537*86d7f5d3SJohn Marino buf_output (protocol, "\n", 1);
4538*86d7f5d3SJohn Marino free (scratched_file);
4539*86d7f5d3SJohn Marino scratched_file = NULL;
4540*86d7f5d3SJohn Marino }
4541*86d7f5d3SJohn Marino else
4542*86d7f5d3SJohn Marino {
4543*86d7f5d3SJohn Marino checked_in_response (file, update_dir, repository);
4544*86d7f5d3SJohn Marino }
4545*86d7f5d3SJohn Marino buf_send_counted (protocol);
4546*86d7f5d3SJohn Marino }
4547*86d7f5d3SJohn Marino
4548*86d7f5d3SJohn Marino
4549*86d7f5d3SJohn Marino
4550*86d7f5d3SJohn Marino void
server_update_entries(const char * file,const char * update_dir,const char * repository,enum server_updated_arg4 updated)4551*86d7f5d3SJohn Marino server_update_entries (const char *file, const char *update_dir,
4552*86d7f5d3SJohn Marino const char *repository,
4553*86d7f5d3SJohn Marino enum server_updated_arg4 updated)
4554*86d7f5d3SJohn Marino {
4555*86d7f5d3SJohn Marino if (noexec)
4556*86d7f5d3SJohn Marino return;
4557*86d7f5d3SJohn Marino if (updated == SERVER_UPDATED)
4558*86d7f5d3SJohn Marino checked_in_response (file, update_dir, repository);
4559*86d7f5d3SJohn Marino else
4560*86d7f5d3SJohn Marino {
4561*86d7f5d3SJohn Marino if (!supported_response ("New-entry"))
4562*86d7f5d3SJohn Marino return;
4563*86d7f5d3SJohn Marino buf_output0 (protocol, "New-entry ");
4564*86d7f5d3SJohn Marino output_dir (update_dir, repository);
4565*86d7f5d3SJohn Marino buf_output0 (protocol, file);
4566*86d7f5d3SJohn Marino buf_output (protocol, "\n", 1);
4567*86d7f5d3SJohn Marino new_entries_line ();
4568*86d7f5d3SJohn Marino }
4569*86d7f5d3SJohn Marino
4570*86d7f5d3SJohn Marino buf_send_counted (protocol);
4571*86d7f5d3SJohn Marino }
4572*86d7f5d3SJohn Marino
4573*86d7f5d3SJohn Marino
4574*86d7f5d3SJohn Marino
4575*86d7f5d3SJohn Marino static void
serve_update(char * arg)4576*86d7f5d3SJohn Marino serve_update (char *arg)
4577*86d7f5d3SJohn Marino {
4578*86d7f5d3SJohn Marino do_cvs_command ("update", update);
4579*86d7f5d3SJohn Marino }
4580*86d7f5d3SJohn Marino
4581*86d7f5d3SJohn Marino
4582*86d7f5d3SJohn Marino
4583*86d7f5d3SJohn Marino static void
serve_diff(char * arg)4584*86d7f5d3SJohn Marino serve_diff (char *arg)
4585*86d7f5d3SJohn Marino {
4586*86d7f5d3SJohn Marino do_cvs_command ("diff", diff);
4587*86d7f5d3SJohn Marino }
4588*86d7f5d3SJohn Marino
4589*86d7f5d3SJohn Marino
4590*86d7f5d3SJohn Marino
4591*86d7f5d3SJohn Marino static void
serve_log(char * arg)4592*86d7f5d3SJohn Marino serve_log (char *arg)
4593*86d7f5d3SJohn Marino {
4594*86d7f5d3SJohn Marino do_cvs_command ("log", cvslog);
4595*86d7f5d3SJohn Marino }
4596*86d7f5d3SJohn Marino
4597*86d7f5d3SJohn Marino
4598*86d7f5d3SJohn Marino
4599*86d7f5d3SJohn Marino static void
serve_rlog(char * arg)4600*86d7f5d3SJohn Marino serve_rlog (char *arg)
4601*86d7f5d3SJohn Marino {
4602*86d7f5d3SJohn Marino do_cvs_command ("rlog", cvslog);
4603*86d7f5d3SJohn Marino }
4604*86d7f5d3SJohn Marino
4605*86d7f5d3SJohn Marino
4606*86d7f5d3SJohn Marino
4607*86d7f5d3SJohn Marino static void
serve_ls(char * arg)4608*86d7f5d3SJohn Marino serve_ls (char *arg)
4609*86d7f5d3SJohn Marino {
4610*86d7f5d3SJohn Marino do_cvs_command ("ls", ls);
4611*86d7f5d3SJohn Marino }
4612*86d7f5d3SJohn Marino
4613*86d7f5d3SJohn Marino
4614*86d7f5d3SJohn Marino
4615*86d7f5d3SJohn Marino static void
serve_rls(char * arg)4616*86d7f5d3SJohn Marino serve_rls (char *arg)
4617*86d7f5d3SJohn Marino {
4618*86d7f5d3SJohn Marino do_cvs_command ("rls", ls);
4619*86d7f5d3SJohn Marino }
4620*86d7f5d3SJohn Marino
4621*86d7f5d3SJohn Marino
4622*86d7f5d3SJohn Marino
4623*86d7f5d3SJohn Marino static void
serve_add(char * arg)4624*86d7f5d3SJohn Marino serve_add (char *arg)
4625*86d7f5d3SJohn Marino {
4626*86d7f5d3SJohn Marino do_cvs_command ("add", add);
4627*86d7f5d3SJohn Marino }
4628*86d7f5d3SJohn Marino
4629*86d7f5d3SJohn Marino
4630*86d7f5d3SJohn Marino
4631*86d7f5d3SJohn Marino static void
serve_remove(char * arg)4632*86d7f5d3SJohn Marino serve_remove (char *arg)
4633*86d7f5d3SJohn Marino {
4634*86d7f5d3SJohn Marino do_cvs_command ("remove", cvsremove);
4635*86d7f5d3SJohn Marino }
4636*86d7f5d3SJohn Marino
4637*86d7f5d3SJohn Marino
4638*86d7f5d3SJohn Marino
4639*86d7f5d3SJohn Marino static void
serve_status(char * arg)4640*86d7f5d3SJohn Marino serve_status (char *arg)
4641*86d7f5d3SJohn Marino {
4642*86d7f5d3SJohn Marino do_cvs_command ("status", cvsstatus);
4643*86d7f5d3SJohn Marino }
4644*86d7f5d3SJohn Marino
4645*86d7f5d3SJohn Marino
4646*86d7f5d3SJohn Marino
4647*86d7f5d3SJohn Marino static void
serve_rdiff(char * arg)4648*86d7f5d3SJohn Marino serve_rdiff (char *arg)
4649*86d7f5d3SJohn Marino {
4650*86d7f5d3SJohn Marino do_cvs_command ("rdiff", patch);
4651*86d7f5d3SJohn Marino }
4652*86d7f5d3SJohn Marino
4653*86d7f5d3SJohn Marino
4654*86d7f5d3SJohn Marino
4655*86d7f5d3SJohn Marino static void
serve_tag(char * arg)4656*86d7f5d3SJohn Marino serve_tag (char *arg)
4657*86d7f5d3SJohn Marino {
4658*86d7f5d3SJohn Marino do_cvs_command ("tag", cvstag);
4659*86d7f5d3SJohn Marino }
4660*86d7f5d3SJohn Marino
4661*86d7f5d3SJohn Marino
4662*86d7f5d3SJohn Marino
4663*86d7f5d3SJohn Marino static void
serve_rtag(char * arg)4664*86d7f5d3SJohn Marino serve_rtag (char *arg)
4665*86d7f5d3SJohn Marino {
4666*86d7f5d3SJohn Marino do_cvs_command ("rtag", cvstag);
4667*86d7f5d3SJohn Marino }
4668*86d7f5d3SJohn Marino
4669*86d7f5d3SJohn Marino
4670*86d7f5d3SJohn Marino
4671*86d7f5d3SJohn Marino static void
serve_import(char * arg)4672*86d7f5d3SJohn Marino serve_import (char *arg)
4673*86d7f5d3SJohn Marino {
4674*86d7f5d3SJohn Marino do_cvs_command ("import", import);
4675*86d7f5d3SJohn Marino }
4676*86d7f5d3SJohn Marino
4677*86d7f5d3SJohn Marino
4678*86d7f5d3SJohn Marino
4679*86d7f5d3SJohn Marino static void
serve_admin(char * arg)4680*86d7f5d3SJohn Marino serve_admin (char *arg)
4681*86d7f5d3SJohn Marino {
4682*86d7f5d3SJohn Marino do_cvs_command ("admin", admin);
4683*86d7f5d3SJohn Marino }
4684*86d7f5d3SJohn Marino
4685*86d7f5d3SJohn Marino
4686*86d7f5d3SJohn Marino
4687*86d7f5d3SJohn Marino static void
serve_history(char * arg)4688*86d7f5d3SJohn Marino serve_history (char *arg)
4689*86d7f5d3SJohn Marino {
4690*86d7f5d3SJohn Marino do_cvs_command ("history", history);
4691*86d7f5d3SJohn Marino }
4692*86d7f5d3SJohn Marino
4693*86d7f5d3SJohn Marino
4694*86d7f5d3SJohn Marino
4695*86d7f5d3SJohn Marino static void
serve_release(char * arg)4696*86d7f5d3SJohn Marino serve_release (char *arg)
4697*86d7f5d3SJohn Marino {
4698*86d7f5d3SJohn Marino do_cvs_command ("release", release);
4699*86d7f5d3SJohn Marino }
4700*86d7f5d3SJohn Marino
4701*86d7f5d3SJohn Marino
4702*86d7f5d3SJohn Marino
4703*86d7f5d3SJohn Marino static void
serve_watch_on(char * arg)4704*86d7f5d3SJohn Marino serve_watch_on (char *arg)
4705*86d7f5d3SJohn Marino {
4706*86d7f5d3SJohn Marino do_cvs_command ("watch", watch_on);
4707*86d7f5d3SJohn Marino }
4708*86d7f5d3SJohn Marino
4709*86d7f5d3SJohn Marino
4710*86d7f5d3SJohn Marino
4711*86d7f5d3SJohn Marino static void
serve_watch_off(char * arg)4712*86d7f5d3SJohn Marino serve_watch_off (char *arg)
4713*86d7f5d3SJohn Marino {
4714*86d7f5d3SJohn Marino do_cvs_command ("watch", watch_off);
4715*86d7f5d3SJohn Marino }
4716*86d7f5d3SJohn Marino
4717*86d7f5d3SJohn Marino
4718*86d7f5d3SJohn Marino
4719*86d7f5d3SJohn Marino static void
serve_watch_add(char * arg)4720*86d7f5d3SJohn Marino serve_watch_add (char *arg)
4721*86d7f5d3SJohn Marino {
4722*86d7f5d3SJohn Marino do_cvs_command ("watch", watch_add);
4723*86d7f5d3SJohn Marino }
4724*86d7f5d3SJohn Marino
4725*86d7f5d3SJohn Marino
4726*86d7f5d3SJohn Marino
4727*86d7f5d3SJohn Marino static void
serve_watch_remove(char * arg)4728*86d7f5d3SJohn Marino serve_watch_remove (char *arg)
4729*86d7f5d3SJohn Marino {
4730*86d7f5d3SJohn Marino do_cvs_command ("watch", watch_remove);
4731*86d7f5d3SJohn Marino }
4732*86d7f5d3SJohn Marino
4733*86d7f5d3SJohn Marino
4734*86d7f5d3SJohn Marino
4735*86d7f5d3SJohn Marino static void
serve_watchers(char * arg)4736*86d7f5d3SJohn Marino serve_watchers (char *arg)
4737*86d7f5d3SJohn Marino {
4738*86d7f5d3SJohn Marino do_cvs_command ("watchers", watchers);
4739*86d7f5d3SJohn Marino }
4740*86d7f5d3SJohn Marino
4741*86d7f5d3SJohn Marino
4742*86d7f5d3SJohn Marino
4743*86d7f5d3SJohn Marino static void
serve_editors(char * arg)4744*86d7f5d3SJohn Marino serve_editors (char *arg)
4745*86d7f5d3SJohn Marino {
4746*86d7f5d3SJohn Marino do_cvs_command ("editors", editors);
4747*86d7f5d3SJohn Marino }
4748*86d7f5d3SJohn Marino
4749*86d7f5d3SJohn Marino
4750*86d7f5d3SJohn Marino
4751*86d7f5d3SJohn Marino static void
serve_edit(char * arg)4752*86d7f5d3SJohn Marino serve_edit (char *arg)
4753*86d7f5d3SJohn Marino {
4754*86d7f5d3SJohn Marino do_cvs_command ("edit", edit);
4755*86d7f5d3SJohn Marino }
4756*86d7f5d3SJohn Marino
4757*86d7f5d3SJohn Marino
4758*86d7f5d3SJohn Marino
4759*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
4760*86d7f5d3SJohn Marino /* We need to handle some of this before reprocessing since it is defined to
4761*86d7f5d3SJohn Marino * send a response and print errors before a Root request is received.
4762*86d7f5d3SJohn Marino */
4763*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
4764*86d7f5d3SJohn Marino static void
serve_noop(char * arg)4765*86d7f5d3SJohn Marino serve_noop (char *arg)
4766*86d7f5d3SJohn Marino {
4767*86d7f5d3SJohn Marino /* Errors could be encountered in the first or second passes, so always
4768*86d7f5d3SJohn Marino * send them to the client.
4769*86d7f5d3SJohn Marino */
4770*86d7f5d3SJohn Marino bool pe = print_pending_error();
4771*86d7f5d3SJohn Marino
4772*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
4773*86d7f5d3SJohn Marino /* The portions below need not be handled until reprocessing anyhow since
4774*86d7f5d3SJohn Marino * there should be no entries or notifications prior to that. */
4775*86d7f5d3SJohn Marino if (!proxy_log)
4776*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
4777*86d7f5d3SJohn Marino {
4778*86d7f5d3SJohn Marino server_write_entries ();
4779*86d7f5d3SJohn Marino if (!pe)
4780*86d7f5d3SJohn Marino (void) server_notify ();
4781*86d7f5d3SJohn Marino }
4782*86d7f5d3SJohn Marino
4783*86d7f5d3SJohn Marino if (!pe
4784*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
4785*86d7f5d3SJohn Marino /* "ok" only goes across in the first pass. */
4786*86d7f5d3SJohn Marino && !reprocessing
4787*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
4788*86d7f5d3SJohn Marino )
4789*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "ok\n");
4790*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
4791*86d7f5d3SJohn Marino }
4792*86d7f5d3SJohn Marino
4793*86d7f5d3SJohn Marino
4794*86d7f5d3SJohn Marino
4795*86d7f5d3SJohn Marino static void
serve_version(char * arg)4796*86d7f5d3SJohn Marino serve_version (char *arg)
4797*86d7f5d3SJohn Marino {
4798*86d7f5d3SJohn Marino do_cvs_command ("version", version);
4799*86d7f5d3SJohn Marino }
4800*86d7f5d3SJohn Marino
4801*86d7f5d3SJohn Marino
4802*86d7f5d3SJohn Marino
4803*86d7f5d3SJohn Marino static void
serve_init(char * arg)4804*86d7f5d3SJohn Marino serve_init (char *arg)
4805*86d7f5d3SJohn Marino {
4806*86d7f5d3SJohn Marino cvsroot_t *saved_parsed_root;
4807*86d7f5d3SJohn Marino
4808*86d7f5d3SJohn Marino if (!ISABSOLUTE (arg))
4809*86d7f5d3SJohn Marino {
4810*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (arg)))
4811*86d7f5d3SJohn Marino sprintf (pending_error_text,
4812*86d7f5d3SJohn Marino "E init %s must be an absolute pathname", arg);
4813*86d7f5d3SJohn Marino }
4814*86d7f5d3SJohn Marino # ifdef AUTH_SERVER_SUPPORT
4815*86d7f5d3SJohn Marino else if (Pserver_Repos != NULL)
4816*86d7f5d3SJohn Marino {
4817*86d7f5d3SJohn Marino if (strcmp (Pserver_Repos, arg) != 0)
4818*86d7f5d3SJohn Marino {
4819*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (Pserver_Repos) + strlen (arg)))
4820*86d7f5d3SJohn Marino /* The explicitness is to aid people who are writing clients.
4821*86d7f5d3SJohn Marino I don't see how this information could help an
4822*86d7f5d3SJohn Marino attacker. */
4823*86d7f5d3SJohn Marino sprintf (pending_error_text, "\
4824*86d7f5d3SJohn Marino E Protocol error: init says \"%s\" but pserver says \"%s\"",
4825*86d7f5d3SJohn Marino arg, Pserver_Repos);
4826*86d7f5d3SJohn Marino }
4827*86d7f5d3SJohn Marino }
4828*86d7f5d3SJohn Marino # endif
4829*86d7f5d3SJohn Marino
4830*86d7f5d3SJohn Marino if (print_pending_error ())
4831*86d7f5d3SJohn Marino return;
4832*86d7f5d3SJohn Marino
4833*86d7f5d3SJohn Marino saved_parsed_root = current_parsed_root;
4834*86d7f5d3SJohn Marino current_parsed_root = local_cvsroot (arg);
4835*86d7f5d3SJohn Marino
4836*86d7f5d3SJohn Marino do_cvs_command ("init", init);
4837*86d7f5d3SJohn Marino
4838*86d7f5d3SJohn Marino /* Do not free CURRENT_PARSED_ROOT since it is still in the cache. */
4839*86d7f5d3SJohn Marino current_parsed_root = saved_parsed_root;
4840*86d7f5d3SJohn Marino }
4841*86d7f5d3SJohn Marino
4842*86d7f5d3SJohn Marino
4843*86d7f5d3SJohn Marino
4844*86d7f5d3SJohn Marino static void
serve_annotate(char * arg)4845*86d7f5d3SJohn Marino serve_annotate (char *arg)
4846*86d7f5d3SJohn Marino {
4847*86d7f5d3SJohn Marino do_cvs_command ("annotate", annotate);
4848*86d7f5d3SJohn Marino }
4849*86d7f5d3SJohn Marino
4850*86d7f5d3SJohn Marino
4851*86d7f5d3SJohn Marino
4852*86d7f5d3SJohn Marino static void
serve_rannotate(char * arg)4853*86d7f5d3SJohn Marino serve_rannotate (char *arg)
4854*86d7f5d3SJohn Marino {
4855*86d7f5d3SJohn Marino do_cvs_command ("rannotate", annotate);
4856*86d7f5d3SJohn Marino }
4857*86d7f5d3SJohn Marino
4858*86d7f5d3SJohn Marino
4859*86d7f5d3SJohn Marino
4860*86d7f5d3SJohn Marino static void
serve_co(char * arg)4861*86d7f5d3SJohn Marino serve_co (char *arg)
4862*86d7f5d3SJohn Marino {
4863*86d7f5d3SJohn Marino if (print_pending_error ())
4864*86d7f5d3SJohn Marino return;
4865*86d7f5d3SJohn Marino
4866*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
4867*86d7f5d3SJohn Marino /* If we are not a secondary server, the write proxy log will already have
4868*86d7f5d3SJohn Marino * been processed.
4869*86d7f5d3SJohn Marino */
4870*86d7f5d3SJohn Marino if (isProxyServer ())
4871*86d7f5d3SJohn Marino {
4872*86d7f5d3SJohn Marino if (reprocessing)
4873*86d7f5d3SJohn Marino reprocessing = false;
4874*86d7f5d3SJohn Marino else if (/* The proxy log may be closed if the client sent a
4875*86d7f5d3SJohn Marino * `Command-prep' request.
4876*86d7f5d3SJohn Marino */
4877*86d7f5d3SJohn Marino proxy_log)
4878*86d7f5d3SJohn Marino {
4879*86d7f5d3SJohn Marino /* Set up the log for reprocessing. */
4880*86d7f5d3SJohn Marino rewind_buf_from_net ();
4881*86d7f5d3SJohn Marino /* And return to the main loop in server(), where we will now find
4882*86d7f5d3SJohn Marino * the logged secondary data and reread it.
4883*86d7f5d3SJohn Marino */
4884*86d7f5d3SJohn Marino return;
4885*86d7f5d3SJohn Marino }
4886*86d7f5d3SJohn Marino }
4887*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
4888*86d7f5d3SJohn Marino
4889*86d7f5d3SJohn Marino /* Compensate for server_export()'s setting of cvs_cmd_name.
4890*86d7f5d3SJohn Marino *
4891*86d7f5d3SJohn Marino * [It probably doesn't matter if do_cvs_command() gets "export"
4892*86d7f5d3SJohn Marino * or "checkout", but we ought to be accurate where possible.]
4893*86d7f5d3SJohn Marino */
4894*86d7f5d3SJohn Marino do_cvs_command (!strcmp (cvs_cmd_name, "export") ? "export" : "checkout",
4895*86d7f5d3SJohn Marino checkout);
4896*86d7f5d3SJohn Marino }
4897*86d7f5d3SJohn Marino
4898*86d7f5d3SJohn Marino
4899*86d7f5d3SJohn Marino
4900*86d7f5d3SJohn Marino static void
serve_export(char * arg)4901*86d7f5d3SJohn Marino serve_export (char *arg)
4902*86d7f5d3SJohn Marino {
4903*86d7f5d3SJohn Marino /* Tell checkout() to behave like export not checkout. */
4904*86d7f5d3SJohn Marino cvs_cmd_name = "export";
4905*86d7f5d3SJohn Marino serve_co (arg);
4906*86d7f5d3SJohn Marino }
4907*86d7f5d3SJohn Marino
4908*86d7f5d3SJohn Marino
4909*86d7f5d3SJohn Marino
4910*86d7f5d3SJohn Marino void
server_copy_file(const char * file,const char * update_dir,const char * repository,const char * newfile)4911*86d7f5d3SJohn Marino server_copy_file (const char *file, const char *update_dir,
4912*86d7f5d3SJohn Marino const char *repository, const char *newfile)
4913*86d7f5d3SJohn Marino {
4914*86d7f5d3SJohn Marino /* At least for now, our practice is to have the server enforce
4915*86d7f5d3SJohn Marino noexec for the repository and the client enforce it for the
4916*86d7f5d3SJohn Marino working directory. This might want more thought, and/or
4917*86d7f5d3SJohn Marino documentation in cvsclient.texi (other responses do it
4918*86d7f5d3SJohn Marino differently). */
4919*86d7f5d3SJohn Marino
4920*86d7f5d3SJohn Marino if (!supported_response ("Copy-file"))
4921*86d7f5d3SJohn Marino return;
4922*86d7f5d3SJohn Marino buf_output0 (protocol, "Copy-file ");
4923*86d7f5d3SJohn Marino output_dir (update_dir, repository);
4924*86d7f5d3SJohn Marino buf_output0 (protocol, file);
4925*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
4926*86d7f5d3SJohn Marino buf_output0 (protocol, newfile);
4927*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
4928*86d7f5d3SJohn Marino }
4929*86d7f5d3SJohn Marino
4930*86d7f5d3SJohn Marino
4931*86d7f5d3SJohn Marino
4932*86d7f5d3SJohn Marino /* See server.h for description. */
4933*86d7f5d3SJohn Marino void
server_modtime(struct file_info * finfo,Vers_TS * vers_ts)4934*86d7f5d3SJohn Marino server_modtime (struct file_info *finfo, Vers_TS *vers_ts)
4935*86d7f5d3SJohn Marino {
4936*86d7f5d3SJohn Marino char date[MAXDATELEN];
4937*86d7f5d3SJohn Marino char outdate[MAXDATELEN];
4938*86d7f5d3SJohn Marino
4939*86d7f5d3SJohn Marino assert (vers_ts->vn_rcs != NULL);
4940*86d7f5d3SJohn Marino
4941*86d7f5d3SJohn Marino if (!supported_response ("Mod-time"))
4942*86d7f5d3SJohn Marino return;
4943*86d7f5d3SJohn Marino
4944*86d7f5d3SJohn Marino if (RCS_getrevtime (finfo->rcs, vers_ts->vn_rcs, date, 0) == (time_t) -1)
4945*86d7f5d3SJohn Marino /* FIXME? should we be printing some kind of warning? For one
4946*86d7f5d3SJohn Marino thing I'm not 100% sure whether this happens in non-error
4947*86d7f5d3SJohn Marino circumstances. */
4948*86d7f5d3SJohn Marino return;
4949*86d7f5d3SJohn Marino date_to_internet (outdate, date);
4950*86d7f5d3SJohn Marino buf_output0 (protocol, "Mod-time ");
4951*86d7f5d3SJohn Marino buf_output0 (protocol, outdate);
4952*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
4953*86d7f5d3SJohn Marino }
4954*86d7f5d3SJohn Marino
4955*86d7f5d3SJohn Marino
4956*86d7f5d3SJohn Marino
4957*86d7f5d3SJohn Marino /* See server.h for description. */
4958*86d7f5d3SJohn Marino void
server_updated(struct file_info * finfo,Vers_TS * vers,enum server_updated_arg4 updated,mode_t mode,unsigned char * checksum,struct buffer * filebuf)4959*86d7f5d3SJohn Marino server_updated (
4960*86d7f5d3SJohn Marino struct file_info *finfo,
4961*86d7f5d3SJohn Marino Vers_TS *vers,
4962*86d7f5d3SJohn Marino enum server_updated_arg4 updated,
4963*86d7f5d3SJohn Marino mode_t mode,
4964*86d7f5d3SJohn Marino unsigned char *checksum,
4965*86d7f5d3SJohn Marino struct buffer *filebuf)
4966*86d7f5d3SJohn Marino {
4967*86d7f5d3SJohn Marino if (noexec)
4968*86d7f5d3SJohn Marino {
4969*86d7f5d3SJohn Marino /* Hmm, maybe if we did the same thing for entries_file, we
4970*86d7f5d3SJohn Marino could get rid of the kludges in server_register and
4971*86d7f5d3SJohn Marino server_scratch which refrain from warning if both
4972*86d7f5d3SJohn Marino Scratch_Entry and Register get called. Maybe. */
4973*86d7f5d3SJohn Marino if (scratched_file)
4974*86d7f5d3SJohn Marino {
4975*86d7f5d3SJohn Marino free (scratched_file);
4976*86d7f5d3SJohn Marino scratched_file = NULL;
4977*86d7f5d3SJohn Marino }
4978*86d7f5d3SJohn Marino buf_send_counted (protocol);
4979*86d7f5d3SJohn Marino return;
4980*86d7f5d3SJohn Marino }
4981*86d7f5d3SJohn Marino
4982*86d7f5d3SJohn Marino if (entries_line != NULL && scratched_file == NULL)
4983*86d7f5d3SJohn Marino {
4984*86d7f5d3SJohn Marino FILE *f;
4985*86d7f5d3SJohn Marino struct buffer_data *list, *last;
4986*86d7f5d3SJohn Marino unsigned long size;
4987*86d7f5d3SJohn Marino char size_text[80];
4988*86d7f5d3SJohn Marino
4989*86d7f5d3SJohn Marino /* The contents of the file will be in one of filebuf,
4990*86d7f5d3SJohn Marino list/last, or here. */
4991*86d7f5d3SJohn Marino unsigned char *file;
4992*86d7f5d3SJohn Marino size_t file_allocated;
4993*86d7f5d3SJohn Marino size_t file_used;
4994*86d7f5d3SJohn Marino
4995*86d7f5d3SJohn Marino if (filebuf != NULL)
4996*86d7f5d3SJohn Marino {
4997*86d7f5d3SJohn Marino size = buf_length (filebuf);
4998*86d7f5d3SJohn Marino if (mode == (mode_t) -1)
4999*86d7f5d3SJohn Marino error (1, 0, "\
5000*86d7f5d3SJohn Marino CVS server internal error: no mode in server_updated");
5001*86d7f5d3SJohn Marino }
5002*86d7f5d3SJohn Marino else
5003*86d7f5d3SJohn Marino {
5004*86d7f5d3SJohn Marino struct stat sb;
5005*86d7f5d3SJohn Marino
5006*86d7f5d3SJohn Marino if (stat (finfo->file, &sb) < 0)
5007*86d7f5d3SJohn Marino {
5008*86d7f5d3SJohn Marino if (existence_error (errno))
5009*86d7f5d3SJohn Marino {
5010*86d7f5d3SJohn Marino /* If we have a sticky tag for a branch on which
5011*86d7f5d3SJohn Marino the file is dead, and cvs update the directory,
5012*86d7f5d3SJohn Marino it gets a T_CHECKOUT but no file. So in this
5013*86d7f5d3SJohn Marino case just forget the whole thing. */
5014*86d7f5d3SJohn Marino free (entries_line);
5015*86d7f5d3SJohn Marino entries_line = NULL;
5016*86d7f5d3SJohn Marino goto done;
5017*86d7f5d3SJohn Marino }
5018*86d7f5d3SJohn Marino error (1, errno, "reading %s", finfo->fullname);
5019*86d7f5d3SJohn Marino }
5020*86d7f5d3SJohn Marino size = sb.st_size;
5021*86d7f5d3SJohn Marino if (mode == (mode_t) -1)
5022*86d7f5d3SJohn Marino {
5023*86d7f5d3SJohn Marino /* FIXME: When we check out files the umask of the
5024*86d7f5d3SJohn Marino server (set in .bashrc if rsh is in use) affects
5025*86d7f5d3SJohn Marino what mode we send, and it shouldn't. */
5026*86d7f5d3SJohn Marino mode = sb.st_mode;
5027*86d7f5d3SJohn Marino }
5028*86d7f5d3SJohn Marino }
5029*86d7f5d3SJohn Marino
5030*86d7f5d3SJohn Marino if (checksum != NULL)
5031*86d7f5d3SJohn Marino {
5032*86d7f5d3SJohn Marino static int checksum_supported = -1;
5033*86d7f5d3SJohn Marino
5034*86d7f5d3SJohn Marino if (checksum_supported == -1)
5035*86d7f5d3SJohn Marino {
5036*86d7f5d3SJohn Marino checksum_supported = supported_response ("Checksum");
5037*86d7f5d3SJohn Marino }
5038*86d7f5d3SJohn Marino
5039*86d7f5d3SJohn Marino if (checksum_supported)
5040*86d7f5d3SJohn Marino {
5041*86d7f5d3SJohn Marino int i;
5042*86d7f5d3SJohn Marino char buf[3];
5043*86d7f5d3SJohn Marino
5044*86d7f5d3SJohn Marino buf_output0 (protocol, "Checksum ");
5045*86d7f5d3SJohn Marino for (i = 0; i < 16; i++)
5046*86d7f5d3SJohn Marino {
5047*86d7f5d3SJohn Marino sprintf (buf, "%02x", (unsigned int) checksum[i]);
5048*86d7f5d3SJohn Marino buf_output0 (protocol, buf);
5049*86d7f5d3SJohn Marino }
5050*86d7f5d3SJohn Marino buf_append_char (protocol, '\n');
5051*86d7f5d3SJohn Marino }
5052*86d7f5d3SJohn Marino }
5053*86d7f5d3SJohn Marino
5054*86d7f5d3SJohn Marino if (updated == SERVER_UPDATED)
5055*86d7f5d3SJohn Marino {
5056*86d7f5d3SJohn Marino Node *node;
5057*86d7f5d3SJohn Marino Entnode *entnode;
5058*86d7f5d3SJohn Marino
5059*86d7f5d3SJohn Marino if (!(supported_response ("Created")
5060*86d7f5d3SJohn Marino && supported_response ("Update-existing")))
5061*86d7f5d3SJohn Marino buf_output0 (protocol, "Updated ");
5062*86d7f5d3SJohn Marino else
5063*86d7f5d3SJohn Marino {
5064*86d7f5d3SJohn Marino assert (vers != NULL);
5065*86d7f5d3SJohn Marino if (vers->ts_user == NULL)
5066*86d7f5d3SJohn Marino buf_output0 (protocol, "Created ");
5067*86d7f5d3SJohn Marino else
5068*86d7f5d3SJohn Marino buf_output0 (protocol, "Update-existing ");
5069*86d7f5d3SJohn Marino }
5070*86d7f5d3SJohn Marino
5071*86d7f5d3SJohn Marino /* Now munge the entries to say that the file is unmodified,
5072*86d7f5d3SJohn Marino in case we end up processing it again (e.g. modules3-6
5073*86d7f5d3SJohn Marino in the testsuite). */
5074*86d7f5d3SJohn Marino node = findnode_fn (finfo->entries, finfo->file);
5075*86d7f5d3SJohn Marino entnode = node->data;
5076*86d7f5d3SJohn Marino free (entnode->timestamp);
5077*86d7f5d3SJohn Marino entnode->timestamp = xstrdup ("=");
5078*86d7f5d3SJohn Marino }
5079*86d7f5d3SJohn Marino else if (updated == SERVER_MERGED)
5080*86d7f5d3SJohn Marino buf_output0 (protocol, "Merged ");
5081*86d7f5d3SJohn Marino else if (updated == SERVER_PATCHED)
5082*86d7f5d3SJohn Marino buf_output0 (protocol, "Patched ");
5083*86d7f5d3SJohn Marino else if (updated == SERVER_RCS_DIFF)
5084*86d7f5d3SJohn Marino buf_output0 (protocol, "Rcs-diff ");
5085*86d7f5d3SJohn Marino else
5086*86d7f5d3SJohn Marino abort ();
5087*86d7f5d3SJohn Marino output_dir (finfo->update_dir, finfo->repository);
5088*86d7f5d3SJohn Marino buf_output0 (protocol, finfo->file);
5089*86d7f5d3SJohn Marino buf_output (protocol, "\n", 1);
5090*86d7f5d3SJohn Marino
5091*86d7f5d3SJohn Marino new_entries_line ();
5092*86d7f5d3SJohn Marino
5093*86d7f5d3SJohn Marino {
5094*86d7f5d3SJohn Marino char *mode_string;
5095*86d7f5d3SJohn Marino
5096*86d7f5d3SJohn Marino mode_string = mode_to_string (mode);
5097*86d7f5d3SJohn Marino buf_output0 (protocol, mode_string);
5098*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
5099*86d7f5d3SJohn Marino free (mode_string);
5100*86d7f5d3SJohn Marino }
5101*86d7f5d3SJohn Marino
5102*86d7f5d3SJohn Marino list = last = NULL;
5103*86d7f5d3SJohn Marino
5104*86d7f5d3SJohn Marino file = NULL;
5105*86d7f5d3SJohn Marino file_allocated = 0;
5106*86d7f5d3SJohn Marino file_used = 0;
5107*86d7f5d3SJohn Marino
5108*86d7f5d3SJohn Marino if (size > 0)
5109*86d7f5d3SJohn Marino {
5110*86d7f5d3SJohn Marino /* Throughout this section we use binary mode to read the
5111*86d7f5d3SJohn Marino file we are sending. The client handles any line ending
5112*86d7f5d3SJohn Marino translation if necessary. */
5113*86d7f5d3SJohn Marino
5114*86d7f5d3SJohn Marino if (file_gzip_level
5115*86d7f5d3SJohn Marino /*
5116*86d7f5d3SJohn Marino * For really tiny files, the gzip process startup
5117*86d7f5d3SJohn Marino * time will outweigh the compression savings. This
5118*86d7f5d3SJohn Marino * might be computable somehow; using 100 here is just
5119*86d7f5d3SJohn Marino * a first approximation.
5120*86d7f5d3SJohn Marino */
5121*86d7f5d3SJohn Marino && size > 100)
5122*86d7f5d3SJohn Marino {
5123*86d7f5d3SJohn Marino /* Basing this routine on read_and_gzip is not a
5124*86d7f5d3SJohn Marino high-performance approach. But it seems easier
5125*86d7f5d3SJohn Marino to code than the alternative (and less
5126*86d7f5d3SJohn Marino vulnerable to subtle bugs). Given that this feature
5127*86d7f5d3SJohn Marino is mainly for compatibility, that is the better
5128*86d7f5d3SJohn Marino tradeoff. */
5129*86d7f5d3SJohn Marino
5130*86d7f5d3SJohn Marino int fd;
5131*86d7f5d3SJohn Marino
5132*86d7f5d3SJohn Marino /* Callers must avoid passing us a buffer if
5133*86d7f5d3SJohn Marino file_gzip_level is set. We could handle this case,
5134*86d7f5d3SJohn Marino but it's not worth it since this case never arises
5135*86d7f5d3SJohn Marino with a current client and server. */
5136*86d7f5d3SJohn Marino if (filebuf != NULL)
5137*86d7f5d3SJohn Marino error (1, 0, "\
5138*86d7f5d3SJohn Marino CVS server internal error: unhandled case in server_updated");
5139*86d7f5d3SJohn Marino
5140*86d7f5d3SJohn Marino fd = CVS_OPEN (finfo->file, O_RDONLY | OPEN_BINARY, 0);
5141*86d7f5d3SJohn Marino if (fd < 0)
5142*86d7f5d3SJohn Marino error (1, errno, "reading %s", finfo->fullname);
5143*86d7f5d3SJohn Marino if (read_and_gzip (fd, finfo->fullname, &file,
5144*86d7f5d3SJohn Marino &file_allocated, &file_used,
5145*86d7f5d3SJohn Marino file_gzip_level))
5146*86d7f5d3SJohn Marino error (1, 0, "aborting due to compression error");
5147*86d7f5d3SJohn Marino size = file_used;
5148*86d7f5d3SJohn Marino if (close (fd) < 0)
5149*86d7f5d3SJohn Marino error (1, errno, "reading %s", finfo->fullname);
5150*86d7f5d3SJohn Marino /* Prepending length with "z" is flag for using gzip here. */
5151*86d7f5d3SJohn Marino buf_output0 (protocol, "z");
5152*86d7f5d3SJohn Marino }
5153*86d7f5d3SJohn Marino else if (filebuf == NULL)
5154*86d7f5d3SJohn Marino {
5155*86d7f5d3SJohn Marino long status;
5156*86d7f5d3SJohn Marino
5157*86d7f5d3SJohn Marino f = CVS_FOPEN (finfo->file, "rb");
5158*86d7f5d3SJohn Marino if (f == NULL)
5159*86d7f5d3SJohn Marino error (1, errno, "reading %s", finfo->fullname);
5160*86d7f5d3SJohn Marino status = buf_read_file (f, size, &list, &last);
5161*86d7f5d3SJohn Marino if (status == -2)
5162*86d7f5d3SJohn Marino (*protocol->memory_error) (protocol);
5163*86d7f5d3SJohn Marino else if (status != 0)
5164*86d7f5d3SJohn Marino error (1, ferror (f) ? errno : 0, "reading %s",
5165*86d7f5d3SJohn Marino finfo->fullname);
5166*86d7f5d3SJohn Marino if (fclose (f) == EOF)
5167*86d7f5d3SJohn Marino error (1, errno, "reading %s", finfo->fullname);
5168*86d7f5d3SJohn Marino }
5169*86d7f5d3SJohn Marino }
5170*86d7f5d3SJohn Marino
5171*86d7f5d3SJohn Marino sprintf (size_text, "%lu\n", size);
5172*86d7f5d3SJohn Marino buf_output0 (protocol, size_text);
5173*86d7f5d3SJohn Marino
5174*86d7f5d3SJohn Marino if (file != NULL)
5175*86d7f5d3SJohn Marino {
5176*86d7f5d3SJohn Marino buf_output (protocol, (char *) file, file_used);
5177*86d7f5d3SJohn Marino free (file);
5178*86d7f5d3SJohn Marino file = NULL;
5179*86d7f5d3SJohn Marino }
5180*86d7f5d3SJohn Marino else if (filebuf == NULL)
5181*86d7f5d3SJohn Marino buf_append_data (protocol, list, last);
5182*86d7f5d3SJohn Marino else
5183*86d7f5d3SJohn Marino buf_append_buffer (protocol, filebuf);
5184*86d7f5d3SJohn Marino /* Note we only send a newline here if the file ended with one. */
5185*86d7f5d3SJohn Marino
5186*86d7f5d3SJohn Marino /*
5187*86d7f5d3SJohn Marino * Avoid using up too much disk space for temporary files.
5188*86d7f5d3SJohn Marino * A file which does not exist indicates that the file is up-to-date,
5189*86d7f5d3SJohn Marino * which is now the case. If this is SERVER_MERGED, the file is
5190*86d7f5d3SJohn Marino * not up-to-date, and we indicate that by leaving the file there.
5191*86d7f5d3SJohn Marino * I'm thinking of cases like "cvs update foo/foo.c foo".
5192*86d7f5d3SJohn Marino */
5193*86d7f5d3SJohn Marino if ((updated == SERVER_UPDATED
5194*86d7f5d3SJohn Marino || updated == SERVER_PATCHED
5195*86d7f5d3SJohn Marino || updated == SERVER_RCS_DIFF)
5196*86d7f5d3SJohn Marino && filebuf == NULL
5197*86d7f5d3SJohn Marino /* But if we are joining, we'll need the file when we call
5198*86d7f5d3SJohn Marino join_file. */
5199*86d7f5d3SJohn Marino && !joining ())
5200*86d7f5d3SJohn Marino {
5201*86d7f5d3SJohn Marino if (CVS_UNLINK (finfo->file) < 0)
5202*86d7f5d3SJohn Marino error (0, errno, "cannot remove temp file for %s",
5203*86d7f5d3SJohn Marino finfo->fullname);
5204*86d7f5d3SJohn Marino }
5205*86d7f5d3SJohn Marino }
5206*86d7f5d3SJohn Marino else if (scratched_file != NULL && entries_line == NULL)
5207*86d7f5d3SJohn Marino {
5208*86d7f5d3SJohn Marino if (strcmp (scratched_file, finfo->file) != 0)
5209*86d7f5d3SJohn Marino error (1, 0,
5210*86d7f5d3SJohn Marino "CVS server internal error: `%s' vs. `%s' scratched",
5211*86d7f5d3SJohn Marino scratched_file,
5212*86d7f5d3SJohn Marino finfo->file);
5213*86d7f5d3SJohn Marino free (scratched_file);
5214*86d7f5d3SJohn Marino scratched_file = NULL;
5215*86d7f5d3SJohn Marino
5216*86d7f5d3SJohn Marino if (kill_scratched_file)
5217*86d7f5d3SJohn Marino buf_output0 (protocol, "Removed ");
5218*86d7f5d3SJohn Marino else
5219*86d7f5d3SJohn Marino buf_output0 (protocol, "Remove-entry ");
5220*86d7f5d3SJohn Marino output_dir (finfo->update_dir, finfo->repository);
5221*86d7f5d3SJohn Marino buf_output0 (protocol, finfo->file);
5222*86d7f5d3SJohn Marino buf_output (protocol, "\n", 1);
5223*86d7f5d3SJohn Marino /* keep the vers structure up to date in case we do a join
5224*86d7f5d3SJohn Marino * - if there isn't a file, it can't very well have a version number,
5225*86d7f5d3SJohn Marino * can it?
5226*86d7f5d3SJohn Marino *
5227*86d7f5d3SJohn Marino * we do it here on the assumption that since we just told the client
5228*86d7f5d3SJohn Marino * to remove the file/entry, it will, and we want to remember that.
5229*86d7f5d3SJohn Marino * If it fails, that's the client's problem, not ours
5230*86d7f5d3SJohn Marino */
5231*86d7f5d3SJohn Marino if (vers && vers->vn_user != NULL)
5232*86d7f5d3SJohn Marino {
5233*86d7f5d3SJohn Marino free (vers->vn_user);
5234*86d7f5d3SJohn Marino vers->vn_user = NULL;
5235*86d7f5d3SJohn Marino }
5236*86d7f5d3SJohn Marino if (vers && vers->ts_user != NULL)
5237*86d7f5d3SJohn Marino {
5238*86d7f5d3SJohn Marino free (vers->ts_user);
5239*86d7f5d3SJohn Marino vers->ts_user = NULL;
5240*86d7f5d3SJohn Marino }
5241*86d7f5d3SJohn Marino }
5242*86d7f5d3SJohn Marino else if (scratched_file == NULL && entries_line == NULL)
5243*86d7f5d3SJohn Marino {
5244*86d7f5d3SJohn Marino /*
5245*86d7f5d3SJohn Marino * This can happen with death support if we were processing
5246*86d7f5d3SJohn Marino * a dead file in a checkout.
5247*86d7f5d3SJohn Marino */
5248*86d7f5d3SJohn Marino }
5249*86d7f5d3SJohn Marino else
5250*86d7f5d3SJohn Marino error (1, 0,
5251*86d7f5d3SJohn Marino "CVS server internal error: Register *and* Scratch_Entry.\n");
5252*86d7f5d3SJohn Marino buf_send_counted (protocol);
5253*86d7f5d3SJohn Marino done:;
5254*86d7f5d3SJohn Marino }
5255*86d7f5d3SJohn Marino
5256*86d7f5d3SJohn Marino
5257*86d7f5d3SJohn Marino
5258*86d7f5d3SJohn Marino /* Return whether we should send patches in RCS format. */
5259*86d7f5d3SJohn Marino int
server_use_rcs_diff(void)5260*86d7f5d3SJohn Marino server_use_rcs_diff (void)
5261*86d7f5d3SJohn Marino {
5262*86d7f5d3SJohn Marino return supported_response ("Rcs-diff");
5263*86d7f5d3SJohn Marino }
5264*86d7f5d3SJohn Marino
5265*86d7f5d3SJohn Marino
5266*86d7f5d3SJohn Marino
5267*86d7f5d3SJohn Marino void
server_set_entstat(const char * update_dir,const char * repository)5268*86d7f5d3SJohn Marino server_set_entstat (const char *update_dir, const char *repository)
5269*86d7f5d3SJohn Marino {
5270*86d7f5d3SJohn Marino static int set_static_supported = -1;
5271*86d7f5d3SJohn Marino if (set_static_supported == -1)
5272*86d7f5d3SJohn Marino set_static_supported = supported_response ("Set-static-directory");
5273*86d7f5d3SJohn Marino if (!set_static_supported) return;
5274*86d7f5d3SJohn Marino
5275*86d7f5d3SJohn Marino buf_output0 (protocol, "Set-static-directory ");
5276*86d7f5d3SJohn Marino output_dir (update_dir, repository);
5277*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
5278*86d7f5d3SJohn Marino buf_send_counted (protocol);
5279*86d7f5d3SJohn Marino }
5280*86d7f5d3SJohn Marino
5281*86d7f5d3SJohn Marino
5282*86d7f5d3SJohn Marino
5283*86d7f5d3SJohn Marino void
server_clear_entstat(const char * update_dir,const char * repository)5284*86d7f5d3SJohn Marino server_clear_entstat (const char *update_dir, const char *repository)
5285*86d7f5d3SJohn Marino {
5286*86d7f5d3SJohn Marino static int clear_static_supported = -1;
5287*86d7f5d3SJohn Marino if (clear_static_supported == -1)
5288*86d7f5d3SJohn Marino clear_static_supported = supported_response ("Clear-static-directory");
5289*86d7f5d3SJohn Marino if (!clear_static_supported) return;
5290*86d7f5d3SJohn Marino
5291*86d7f5d3SJohn Marino if (noexec)
5292*86d7f5d3SJohn Marino return;
5293*86d7f5d3SJohn Marino
5294*86d7f5d3SJohn Marino buf_output0 (protocol, "Clear-static-directory ");
5295*86d7f5d3SJohn Marino output_dir (update_dir, repository);
5296*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
5297*86d7f5d3SJohn Marino buf_send_counted (protocol);
5298*86d7f5d3SJohn Marino }
5299*86d7f5d3SJohn Marino
5300*86d7f5d3SJohn Marino
5301*86d7f5d3SJohn Marino
5302*86d7f5d3SJohn Marino void
server_set_sticky(const char * update_dir,const char * repository,const char * tag,const char * date,int nonbranch)5303*86d7f5d3SJohn Marino server_set_sticky (const char *update_dir, const char *repository,
5304*86d7f5d3SJohn Marino const char *tag, const char *date, int nonbranch)
5305*86d7f5d3SJohn Marino {
5306*86d7f5d3SJohn Marino static int set_sticky_supported = -1;
5307*86d7f5d3SJohn Marino
5308*86d7f5d3SJohn Marino assert (update_dir != NULL);
5309*86d7f5d3SJohn Marino
5310*86d7f5d3SJohn Marino if (set_sticky_supported == -1)
5311*86d7f5d3SJohn Marino set_sticky_supported = supported_response ("Set-sticky");
5312*86d7f5d3SJohn Marino if (!set_sticky_supported) return;
5313*86d7f5d3SJohn Marino
5314*86d7f5d3SJohn Marino if (noexec)
5315*86d7f5d3SJohn Marino return;
5316*86d7f5d3SJohn Marino
5317*86d7f5d3SJohn Marino if (tag == NULL && date == NULL)
5318*86d7f5d3SJohn Marino {
5319*86d7f5d3SJohn Marino buf_output0 (protocol, "Clear-sticky ");
5320*86d7f5d3SJohn Marino output_dir (update_dir, repository);
5321*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
5322*86d7f5d3SJohn Marino }
5323*86d7f5d3SJohn Marino else
5324*86d7f5d3SJohn Marino {
5325*86d7f5d3SJohn Marino buf_output0 (protocol, "Set-sticky ");
5326*86d7f5d3SJohn Marino output_dir (update_dir, repository);
5327*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
5328*86d7f5d3SJohn Marino if (tag != NULL)
5329*86d7f5d3SJohn Marino {
5330*86d7f5d3SJohn Marino if (nonbranch)
5331*86d7f5d3SJohn Marino buf_output0 (protocol, "N");
5332*86d7f5d3SJohn Marino else
5333*86d7f5d3SJohn Marino buf_output0 (protocol, "T");
5334*86d7f5d3SJohn Marino buf_output0 (protocol, tag);
5335*86d7f5d3SJohn Marino }
5336*86d7f5d3SJohn Marino else
5337*86d7f5d3SJohn Marino {
5338*86d7f5d3SJohn Marino buf_output0 (protocol, "D");
5339*86d7f5d3SJohn Marino buf_output0 (protocol, date);
5340*86d7f5d3SJohn Marino }
5341*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
5342*86d7f5d3SJohn Marino }
5343*86d7f5d3SJohn Marino buf_send_counted (protocol);
5344*86d7f5d3SJohn Marino }
5345*86d7f5d3SJohn Marino
5346*86d7f5d3SJohn Marino
5347*86d7f5d3SJohn Marino
5348*86d7f5d3SJohn Marino void
server_edit_file(struct file_info * finfo)5349*86d7f5d3SJohn Marino server_edit_file (struct file_info *finfo)
5350*86d7f5d3SJohn Marino {
5351*86d7f5d3SJohn Marino buf_output (protocol, "Edit-file ", 10);
5352*86d7f5d3SJohn Marino output_dir (finfo->update_dir, finfo->repository);
5353*86d7f5d3SJohn Marino buf_output0 (protocol, finfo->file);
5354*86d7f5d3SJohn Marino buf_output (protocol, "\n", 1);
5355*86d7f5d3SJohn Marino buf_send_counted (protocol);
5356*86d7f5d3SJohn Marino }
5357*86d7f5d3SJohn Marino
5358*86d7f5d3SJohn Marino
5359*86d7f5d3SJohn Marino
5360*86d7f5d3SJohn Marino struct template_proc_data
5361*86d7f5d3SJohn Marino {
5362*86d7f5d3SJohn Marino const char *update_dir;
5363*86d7f5d3SJohn Marino const char *repository;
5364*86d7f5d3SJohn Marino };
5365*86d7f5d3SJohn Marino
5366*86d7f5d3SJohn Marino static int
template_proc(const char * repository,const char * template,void * closure)5367*86d7f5d3SJohn Marino template_proc (const char *repository, const char *template, void *closure)
5368*86d7f5d3SJohn Marino {
5369*86d7f5d3SJohn Marino FILE *fp;
5370*86d7f5d3SJohn Marino char buf[1024];
5371*86d7f5d3SJohn Marino size_t n;
5372*86d7f5d3SJohn Marino struct stat sb;
5373*86d7f5d3SJohn Marino struct template_proc_data *data = (struct template_proc_data *)closure;
5374*86d7f5d3SJohn Marino
5375*86d7f5d3SJohn Marino if (!supported_response ("Template"))
5376*86d7f5d3SJohn Marino /* Might want to warn the user that the rcsinfo feature won't work. */
5377*86d7f5d3SJohn Marino return 0;
5378*86d7f5d3SJohn Marino buf_output0 (protocol, "Template ");
5379*86d7f5d3SJohn Marino output_dir (data->update_dir, data->repository);
5380*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
5381*86d7f5d3SJohn Marino
5382*86d7f5d3SJohn Marino fp = CVS_FOPEN (template, "rb");
5383*86d7f5d3SJohn Marino if (fp == NULL)
5384*86d7f5d3SJohn Marino {
5385*86d7f5d3SJohn Marino error (0, errno, "Couldn't open rcsinfo template file %s", template);
5386*86d7f5d3SJohn Marino return 1;
5387*86d7f5d3SJohn Marino }
5388*86d7f5d3SJohn Marino if (fstat (fileno (fp), &sb) < 0)
5389*86d7f5d3SJohn Marino {
5390*86d7f5d3SJohn Marino error (0, errno, "cannot stat rcsinfo template file %s", template);
5391*86d7f5d3SJohn Marino return 1;
5392*86d7f5d3SJohn Marino }
5393*86d7f5d3SJohn Marino sprintf (buf, "%ld\n", (long) sb.st_size);
5394*86d7f5d3SJohn Marino buf_output0 (protocol, buf);
5395*86d7f5d3SJohn Marino while (!feof (fp))
5396*86d7f5d3SJohn Marino {
5397*86d7f5d3SJohn Marino n = fread (buf, 1, sizeof buf, fp);
5398*86d7f5d3SJohn Marino buf_output (protocol, buf, n);
5399*86d7f5d3SJohn Marino if (ferror (fp))
5400*86d7f5d3SJohn Marino {
5401*86d7f5d3SJohn Marino error (0, errno, "cannot read rcsinfo template file %s", template);
5402*86d7f5d3SJohn Marino (void) fclose (fp);
5403*86d7f5d3SJohn Marino return 1;
5404*86d7f5d3SJohn Marino }
5405*86d7f5d3SJohn Marino }
5406*86d7f5d3SJohn Marino buf_send_counted (protocol);
5407*86d7f5d3SJohn Marino if (fclose (fp) < 0)
5408*86d7f5d3SJohn Marino error (0, errno, "cannot close rcsinfo template file %s", template);
5409*86d7f5d3SJohn Marino return 0;
5410*86d7f5d3SJohn Marino }
5411*86d7f5d3SJohn Marino
5412*86d7f5d3SJohn Marino
5413*86d7f5d3SJohn Marino
5414*86d7f5d3SJohn Marino void
server_clear_template(const char * update_dir,const char * repository)5415*86d7f5d3SJohn Marino server_clear_template (const char *update_dir, const char *repository)
5416*86d7f5d3SJohn Marino {
5417*86d7f5d3SJohn Marino assert (update_dir != NULL);
5418*86d7f5d3SJohn Marino
5419*86d7f5d3SJohn Marino if (noexec)
5420*86d7f5d3SJohn Marino return;
5421*86d7f5d3SJohn Marino
5422*86d7f5d3SJohn Marino if (!supported_response ("Clear-template") &&
5423*86d7f5d3SJohn Marino !supported_response ("Template"))
5424*86d7f5d3SJohn Marino /* Might want to warn the user that the rcsinfo feature won't work. */
5425*86d7f5d3SJohn Marino return;
5426*86d7f5d3SJohn Marino
5427*86d7f5d3SJohn Marino if (supported_response ("Clear-template"))
5428*86d7f5d3SJohn Marino {
5429*86d7f5d3SJohn Marino buf_output0 (protocol, "Clear-template ");
5430*86d7f5d3SJohn Marino output_dir (update_dir, repository);
5431*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
5432*86d7f5d3SJohn Marino buf_send_counted (protocol);
5433*86d7f5d3SJohn Marino }
5434*86d7f5d3SJohn Marino else
5435*86d7f5d3SJohn Marino {
5436*86d7f5d3SJohn Marino buf_output0 (protocol, "Template ");
5437*86d7f5d3SJohn Marino output_dir (update_dir, repository);
5438*86d7f5d3SJohn Marino buf_output0 (protocol, "\n");
5439*86d7f5d3SJohn Marino buf_output0 (protocol, "0\n");
5440*86d7f5d3SJohn Marino buf_send_counted (protocol);
5441*86d7f5d3SJohn Marino }
5442*86d7f5d3SJohn Marino }
5443*86d7f5d3SJohn Marino
5444*86d7f5d3SJohn Marino
5445*86d7f5d3SJohn Marino
5446*86d7f5d3SJohn Marino void
server_template(const char * update_dir,const char * repository)5447*86d7f5d3SJohn Marino server_template (const char *update_dir, const char *repository)
5448*86d7f5d3SJohn Marino {
5449*86d7f5d3SJohn Marino struct template_proc_data data;
5450*86d7f5d3SJohn Marino data.update_dir = update_dir;
5451*86d7f5d3SJohn Marino data.repository = repository;
5452*86d7f5d3SJohn Marino (void) Parse_Info (CVSROOTADM_RCSINFO, repository, template_proc,
5453*86d7f5d3SJohn Marino PIOPT_ALL, &data);
5454*86d7f5d3SJohn Marino }
5455*86d7f5d3SJohn Marino
5456*86d7f5d3SJohn Marino
5457*86d7f5d3SJohn Marino
5458*86d7f5d3SJohn Marino static void
serve_gzip_contents(char * arg)5459*86d7f5d3SJohn Marino serve_gzip_contents (char *arg)
5460*86d7f5d3SJohn Marino {
5461*86d7f5d3SJohn Marino int level;
5462*86d7f5d3SJohn Marino bool forced = false;
5463*86d7f5d3SJohn Marino
5464*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5465*86d7f5d3SJohn Marino assert (!proxy_log);
5466*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5467*86d7f5d3SJohn Marino
5468*86d7f5d3SJohn Marino level = atoi (arg);
5469*86d7f5d3SJohn Marino if (level == 0)
5470*86d7f5d3SJohn Marino level = 6;
5471*86d7f5d3SJohn Marino
5472*86d7f5d3SJohn Marino if (config && level < config->MinCompressionLevel)
5473*86d7f5d3SJohn Marino {
5474*86d7f5d3SJohn Marino level = config->MinCompressionLevel;
5475*86d7f5d3SJohn Marino forced = true;
5476*86d7f5d3SJohn Marino }
5477*86d7f5d3SJohn Marino if (config && level > config->MaxCompressionLevel)
5478*86d7f5d3SJohn Marino {
5479*86d7f5d3SJohn Marino level = config->MaxCompressionLevel;
5480*86d7f5d3SJohn Marino forced = true;
5481*86d7f5d3SJohn Marino }
5482*86d7f5d3SJohn Marino
5483*86d7f5d3SJohn Marino if (forced && !quiet
5484*86d7f5d3SJohn Marino && alloc_pending_warning (120 + strlen (program_name)))
5485*86d7f5d3SJohn Marino sprintf (pending_warning_text,
5486*86d7f5d3SJohn Marino "E %s server: Forcing compression level %d (allowed: %d <= z <= %d).",
5487*86d7f5d3SJohn Marino program_name, level, config->MinCompressionLevel,
5488*86d7f5d3SJohn Marino config->MaxCompressionLevel);
5489*86d7f5d3SJohn Marino
5490*86d7f5d3SJohn Marino gzip_level = file_gzip_level = level;
5491*86d7f5d3SJohn Marino }
5492*86d7f5d3SJohn Marino
5493*86d7f5d3SJohn Marino
5494*86d7f5d3SJohn Marino
5495*86d7f5d3SJohn Marino static void
serve_gzip_stream(char * arg)5496*86d7f5d3SJohn Marino serve_gzip_stream (char *arg)
5497*86d7f5d3SJohn Marino {
5498*86d7f5d3SJohn Marino int level;
5499*86d7f5d3SJohn Marino bool forced = false;
5500*86d7f5d3SJohn Marino
5501*86d7f5d3SJohn Marino level = atoi (arg);
5502*86d7f5d3SJohn Marino
5503*86d7f5d3SJohn Marino if (config && level < config->MinCompressionLevel)
5504*86d7f5d3SJohn Marino {
5505*86d7f5d3SJohn Marino level = config->MinCompressionLevel;
5506*86d7f5d3SJohn Marino forced = true;
5507*86d7f5d3SJohn Marino }
5508*86d7f5d3SJohn Marino if (config && level > config->MaxCompressionLevel)
5509*86d7f5d3SJohn Marino {
5510*86d7f5d3SJohn Marino level = config->MaxCompressionLevel;
5511*86d7f5d3SJohn Marino forced = true;
5512*86d7f5d3SJohn Marino }
5513*86d7f5d3SJohn Marino
5514*86d7f5d3SJohn Marino if (forced && !quiet
5515*86d7f5d3SJohn Marino && alloc_pending_warning (120 + strlen (program_name)))
5516*86d7f5d3SJohn Marino sprintf (pending_warning_text,
5517*86d7f5d3SJohn Marino "E %s server: Forcing compression level %d (allowed: %d <= z <= %d).",
5518*86d7f5d3SJohn Marino program_name, level, config->MinCompressionLevel,
5519*86d7f5d3SJohn Marino config->MaxCompressionLevel);
5520*86d7f5d3SJohn Marino
5521*86d7f5d3SJohn Marino gzip_level = level;
5522*86d7f5d3SJohn Marino
5523*86d7f5d3SJohn Marino /* All further communication with the client will be compressed.
5524*86d7f5d3SJohn Marino *
5525*86d7f5d3SJohn Marino * The deflate buffers need to be initialized even for compression level
5526*86d7f5d3SJohn Marino * 0, or the client will no longer be able to understand us. At
5527*86d7f5d3SJohn Marino * compression level 0, the correct compression headers will be created and
5528*86d7f5d3SJohn Marino * sent, but data will thereafter simply be copied to the network buffers.
5529*86d7f5d3SJohn Marino */
5530*86d7f5d3SJohn Marino
5531*86d7f5d3SJohn Marino /* This needs to be processed in both passes so that we may continue to
5532*86d7f5d3SJohn Marino * understand client requests on both the socket and from the log.
5533*86d7f5d3SJohn Marino */
5534*86d7f5d3SJohn Marino buf_from_net = compress_buffer_initialize (buf_from_net, 1,
5535*86d7f5d3SJohn Marino 0 /* Not used. */,
5536*86d7f5d3SJohn Marino buf_from_net->memory_error);
5537*86d7f5d3SJohn Marino
5538*86d7f5d3SJohn Marino /* This needs to be skipped in subsequent passes to avoid compressing data
5539*86d7f5d3SJohn Marino * to the client twice.
5540*86d7f5d3SJohn Marino */
5541*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5542*86d7f5d3SJohn Marino if (reprocessing) return;
5543*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5544*86d7f5d3SJohn Marino buf_to_net = compress_buffer_initialize (buf_to_net, 0, level,
5545*86d7f5d3SJohn Marino buf_to_net->memory_error);
5546*86d7f5d3SJohn Marino }
5547*86d7f5d3SJohn Marino
5548*86d7f5d3SJohn Marino
5549*86d7f5d3SJohn Marino
5550*86d7f5d3SJohn Marino /* Tell the client about RCS options set in CVSROOT/cvswrappers. */
5551*86d7f5d3SJohn Marino static void
serve_wrapper_sendme_rcs_options(char * arg)5552*86d7f5d3SJohn Marino serve_wrapper_sendme_rcs_options (char *arg)
5553*86d7f5d3SJohn Marino {
5554*86d7f5d3SJohn Marino /* Actually, this is kind of sdrawkcab-ssa: the client wants
5555*86d7f5d3SJohn Marino * verbatim lines from a cvswrappers file, but the server has
5556*86d7f5d3SJohn Marino * already parsed the cvswrappers file into the wrap_list struct.
5557*86d7f5d3SJohn Marino * Therefore, the server loops over wrap_list, unparsing each
5558*86d7f5d3SJohn Marino * entry before sending it.
5559*86d7f5d3SJohn Marino */
5560*86d7f5d3SJohn Marino char *wrapper_line = NULL;
5561*86d7f5d3SJohn Marino
5562*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5563*86d7f5d3SJohn Marino if (reprocessing) return;
5564*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5565*86d7f5d3SJohn Marino
5566*86d7f5d3SJohn Marino wrap_setup ();
5567*86d7f5d3SJohn Marino
5568*86d7f5d3SJohn Marino for (wrap_unparse_rcs_options (&wrapper_line, 1);
5569*86d7f5d3SJohn Marino wrapper_line;
5570*86d7f5d3SJohn Marino wrap_unparse_rcs_options (&wrapper_line, 0))
5571*86d7f5d3SJohn Marino {
5572*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "Wrapper-rcsOption ");
5573*86d7f5d3SJohn Marino buf_output0 (buf_to_net, wrapper_line);
5574*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "\012");;
5575*86d7f5d3SJohn Marino free (wrapper_line);
5576*86d7f5d3SJohn Marino }
5577*86d7f5d3SJohn Marino
5578*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "ok\012");
5579*86d7f5d3SJohn Marino
5580*86d7f5d3SJohn Marino /* The client is waiting for us, so we better send the data now. */
5581*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
5582*86d7f5d3SJohn Marino }
5583*86d7f5d3SJohn Marino
5584*86d7f5d3SJohn Marino
5585*86d7f5d3SJohn Marino
5586*86d7f5d3SJohn Marino static void
serve_ignore(char * arg)5587*86d7f5d3SJohn Marino serve_ignore (char *arg)
5588*86d7f5d3SJohn Marino {
5589*86d7f5d3SJohn Marino /*
5590*86d7f5d3SJohn Marino * Just ignore this command. This is used to support the
5591*86d7f5d3SJohn Marino * update-patches command, which is not a real command, but a signal
5592*86d7f5d3SJohn Marino * to the client that update will accept the -u argument.
5593*86d7f5d3SJohn Marino */
5594*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5595*86d7f5d3SJohn Marino assert (!proxy_log);
5596*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5597*86d7f5d3SJohn Marino }
5598*86d7f5d3SJohn Marino
5599*86d7f5d3SJohn Marino
5600*86d7f5d3SJohn Marino
5601*86d7f5d3SJohn Marino static int
expand_proc(int argc,char ** argv,char * where,char * mwhere,char * mfile,int shorten,int local_specified,char * omodule,char * msg)5602*86d7f5d3SJohn Marino expand_proc (int argc, char **argv, char *where, char *mwhere, char *mfile, int shorten, int local_specified, char *omodule, char *msg)
5603*86d7f5d3SJohn Marino {
5604*86d7f5d3SJohn Marino int i;
5605*86d7f5d3SJohn Marino char *dir = argv[0];
5606*86d7f5d3SJohn Marino
5607*86d7f5d3SJohn Marino /* If mwhere has been specified, the thing we're expanding is a
5608*86d7f5d3SJohn Marino module -- just return its name so the client will ask for the
5609*86d7f5d3SJohn Marino right thing later. If it is an alias or a real directory,
5610*86d7f5d3SJohn Marino mwhere will not be set, so send out the appropriate
5611*86d7f5d3SJohn Marino expansion. */
5612*86d7f5d3SJohn Marino
5613*86d7f5d3SJohn Marino if (mwhere != NULL)
5614*86d7f5d3SJohn Marino {
5615*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "Module-expansion ");
5616*86d7f5d3SJohn Marino if (server_dir != NULL)
5617*86d7f5d3SJohn Marino {
5618*86d7f5d3SJohn Marino buf_output0 (buf_to_net, server_dir);
5619*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "/");
5620*86d7f5d3SJohn Marino }
5621*86d7f5d3SJohn Marino buf_output0 (buf_to_net, mwhere);
5622*86d7f5d3SJohn Marino if (mfile != NULL)
5623*86d7f5d3SJohn Marino {
5624*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '/');
5625*86d7f5d3SJohn Marino buf_output0 (buf_to_net, mfile);
5626*86d7f5d3SJohn Marino }
5627*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
5628*86d7f5d3SJohn Marino }
5629*86d7f5d3SJohn Marino else
5630*86d7f5d3SJohn Marino {
5631*86d7f5d3SJohn Marino /* We may not need to do this anymore -- check the definition
5632*86d7f5d3SJohn Marino of aliases before removing */
5633*86d7f5d3SJohn Marino if (argc == 1)
5634*86d7f5d3SJohn Marino {
5635*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "Module-expansion ");
5636*86d7f5d3SJohn Marino if (server_dir != NULL)
5637*86d7f5d3SJohn Marino {
5638*86d7f5d3SJohn Marino buf_output0 (buf_to_net, server_dir);
5639*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "/");
5640*86d7f5d3SJohn Marino }
5641*86d7f5d3SJohn Marino buf_output0 (buf_to_net, dir);
5642*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
5643*86d7f5d3SJohn Marino }
5644*86d7f5d3SJohn Marino else
5645*86d7f5d3SJohn Marino {
5646*86d7f5d3SJohn Marino for (i = 1; i < argc; ++i)
5647*86d7f5d3SJohn Marino {
5648*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "Module-expansion ");
5649*86d7f5d3SJohn Marino if (server_dir != NULL)
5650*86d7f5d3SJohn Marino {
5651*86d7f5d3SJohn Marino buf_output0 (buf_to_net, server_dir);
5652*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "/");
5653*86d7f5d3SJohn Marino }
5654*86d7f5d3SJohn Marino buf_output0 (buf_to_net, dir);
5655*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '/');
5656*86d7f5d3SJohn Marino buf_output0 (buf_to_net, argv[i]);
5657*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
5658*86d7f5d3SJohn Marino }
5659*86d7f5d3SJohn Marino }
5660*86d7f5d3SJohn Marino }
5661*86d7f5d3SJohn Marino return 0;
5662*86d7f5d3SJohn Marino }
5663*86d7f5d3SJohn Marino
5664*86d7f5d3SJohn Marino
5665*86d7f5d3SJohn Marino
5666*86d7f5d3SJohn Marino static void
serve_expand_modules(char * arg)5667*86d7f5d3SJohn Marino serve_expand_modules (char *arg)
5668*86d7f5d3SJohn Marino {
5669*86d7f5d3SJohn Marino int i;
5670*86d7f5d3SJohn Marino int err = 0;
5671*86d7f5d3SJohn Marino DBM *db;
5672*86d7f5d3SJohn Marino
5673*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5674*86d7f5d3SJohn Marino /* This needs to be processed in the first pass since the client expects a
5675*86d7f5d3SJohn Marino * response but we may not yet know if we are a secondary.
5676*86d7f5d3SJohn Marino *
5677*86d7f5d3SJohn Marino * On the second pass, we still must make sure to ignore the arguments.
5678*86d7f5d3SJohn Marino */
5679*86d7f5d3SJohn Marino if (!reprocessing)
5680*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5681*86d7f5d3SJohn Marino {
5682*86d7f5d3SJohn Marino err = 0;
5683*86d7f5d3SJohn Marino
5684*86d7f5d3SJohn Marino db = open_module ();
5685*86d7f5d3SJohn Marino for (i = 1; i < argument_count; i++)
5686*86d7f5d3SJohn Marino err += do_module (db, argument_vector[i],
5687*86d7f5d3SJohn Marino CHECKOUT, "Updating", expand_proc,
5688*86d7f5d3SJohn Marino NULL, 0, 0, 0, 0, NULL);
5689*86d7f5d3SJohn Marino close_module (db);
5690*86d7f5d3SJohn Marino }
5691*86d7f5d3SJohn Marino
5692*86d7f5d3SJohn Marino {
5693*86d7f5d3SJohn Marino /* argument_vector[0] is a dummy argument, we don't mess with it. */
5694*86d7f5d3SJohn Marino char **cp;
5695*86d7f5d3SJohn Marino for (cp = argument_vector + 1;
5696*86d7f5d3SJohn Marino cp < argument_vector + argument_count;
5697*86d7f5d3SJohn Marino ++cp)
5698*86d7f5d3SJohn Marino free (*cp);
5699*86d7f5d3SJohn Marino
5700*86d7f5d3SJohn Marino argument_count = 1;
5701*86d7f5d3SJohn Marino }
5702*86d7f5d3SJohn Marino
5703*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5704*86d7f5d3SJohn Marino if (!reprocessing)
5705*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5706*86d7f5d3SJohn Marino {
5707*86d7f5d3SJohn Marino if (err)
5708*86d7f5d3SJohn Marino /* We will have printed an error message already. */
5709*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "error \n");
5710*86d7f5d3SJohn Marino else
5711*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "ok\n");
5712*86d7f5d3SJohn Marino
5713*86d7f5d3SJohn Marino /* The client is waiting for the module expansions, so we must
5714*86d7f5d3SJohn Marino send the output now. */
5715*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
5716*86d7f5d3SJohn Marino }
5717*86d7f5d3SJohn Marino }
5718*86d7f5d3SJohn Marino
5719*86d7f5d3SJohn Marino
5720*86d7f5d3SJohn Marino
5721*86d7f5d3SJohn Marino /* Decide if we should redirect the client to another server.
5722*86d7f5d3SJohn Marino *
5723*86d7f5d3SJohn Marino * GLOBALS
5724*86d7f5d3SJohn Marino * config->PrimaryServer The server to redirect write requests to, if
5725*86d7f5d3SJohn Marino * any.
5726*86d7f5d3SJohn Marino *
5727*86d7f5d3SJohn Marino * ASSUMPTIONS
5728*86d7f5d3SJohn Marino * The `Root' request has already been processed.
5729*86d7f5d3SJohn Marino *
5730*86d7f5d3SJohn Marino * RETURNS
5731*86d7f5d3SJohn Marino * Nothing.
5732*86d7f5d3SJohn Marino */
5733*86d7f5d3SJohn Marino static void
serve_command_prep(char * arg)5734*86d7f5d3SJohn Marino serve_command_prep (char *arg)
5735*86d7f5d3SJohn Marino {
5736*86d7f5d3SJohn Marino bool redirect_supported;
5737*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5738*86d7f5d3SJohn Marino bool ditch_log;
5739*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5740*86d7f5d3SJohn Marino
5741*86d7f5d3SJohn Marino if (print_pending_error ()) return;
5742*86d7f5d3SJohn Marino
5743*86d7f5d3SJohn Marino redirect_supported = supported_response ("Redirect");
5744*86d7f5d3SJohn Marino if (redirect_supported
5745*86d7f5d3SJohn Marino && lookup_command_attribute (arg) & CVS_CMD_MODIFIES_REPOSITORY
5746*86d7f5d3SJohn Marino /* I call isProxyServer() last because it can probably be the slowest
5747*86d7f5d3SJohn Marino * call due to the call to gethostbyname().
5748*86d7f5d3SJohn Marino */
5749*86d7f5d3SJohn Marino && isProxyServer ())
5750*86d7f5d3SJohn Marino {
5751*86d7f5d3SJohn Marino /* Before sending a redirect, send a "Referrer" line to the client,
5752*86d7f5d3SJohn Marino * if possible, to give admins more control over canonicalizing roots
5753*86d7f5d3SJohn Marino * sent from the client.
5754*86d7f5d3SJohn Marino */
5755*86d7f5d3SJohn Marino if (supported_response ("Referrer"))
5756*86d7f5d3SJohn Marino {
5757*86d7f5d3SJohn Marino /* assume :ext:, since that is all we currently support for
5758*86d7f5d3SJohn Marino * proxies and redirection.
5759*86d7f5d3SJohn Marino */
5760*86d7f5d3SJohn Marino char *referrer = Xasprintf (":ext:%s@%s%s", getcaller(),
5761*86d7f5d3SJohn Marino server_hostname,
5762*86d7f5d3SJohn Marino current_parsed_root->directory);
5763*86d7f5d3SJohn Marino
5764*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "Referrer ");
5765*86d7f5d3SJohn Marino buf_output0 (buf_to_net, referrer);
5766*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "\n");
5767*86d7f5d3SJohn Marino
5768*86d7f5d3SJohn Marino free (referrer);
5769*86d7f5d3SJohn Marino }
5770*86d7f5d3SJohn Marino
5771*86d7f5d3SJohn Marino /* Send `Redirect' to redirect client requests to the primary. */
5772*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "Redirect ");
5773*86d7f5d3SJohn Marino buf_output0 (buf_to_net, config->PrimaryServer->original);
5774*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "\n");
5775*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
5776*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5777*86d7f5d3SJohn Marino ditch_log = true;
5778*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5779*86d7f5d3SJohn Marino }
5780*86d7f5d3SJohn Marino else
5781*86d7f5d3SJohn Marino {
5782*86d7f5d3SJohn Marino /* Send `ok' so the client can proceed. */
5783*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "ok\n");
5784*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
5785*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5786*86d7f5d3SJohn Marino if (lookup_command_attribute (arg) & CVS_CMD_MODIFIES_REPOSITORY
5787*86d7f5d3SJohn Marino && isProxyServer ())
5788*86d7f5d3SJohn Marino /* Don't ditch the log for write commands on a proxy server. We
5789*86d7f5d3SJohn Marino * we got here because the `Redirect' response was not supported.
5790*86d7f5d3SJohn Marino */
5791*86d7f5d3SJohn Marino ditch_log = false;
5792*86d7f5d3SJohn Marino else
5793*86d7f5d3SJohn Marino ditch_log = true;
5794*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5795*86d7f5d3SJohn Marino }
5796*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
5797*86d7f5d3SJohn Marino if (proxy_log && ditch_log)
5798*86d7f5d3SJohn Marino {
5799*86d7f5d3SJohn Marino /* If the client supported the redirect response, then they will always
5800*86d7f5d3SJohn Marino * be redirected if they are preparing for a write request. It is
5801*86d7f5d3SJohn Marino * therefore safe to close the proxy logs.
5802*86d7f5d3SJohn Marino *
5803*86d7f5d3SJohn Marino * If the client is broken and ignores the redirect, this will be
5804*86d7f5d3SJohn Marino * detected later, in rewind_buf_from_net().
5805*86d7f5d3SJohn Marino *
5806*86d7f5d3SJohn Marino * Since a `Command-prep' response is only acceptable immediately
5807*86d7f5d3SJohn Marino * following the `Root' request according to the specification, there
5808*86d7f5d3SJohn Marino * is no need to rewind the log and reprocess.
5809*86d7f5d3SJohn Marino */
5810*86d7f5d3SJohn Marino log_buffer_closelog (proxy_log);
5811*86d7f5d3SJohn Marino log_buffer_closelog (proxy_log_out);
5812*86d7f5d3SJohn Marino proxy_log = NULL;
5813*86d7f5d3SJohn Marino }
5814*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
5815*86d7f5d3SJohn Marino }
5816*86d7f5d3SJohn Marino
5817*86d7f5d3SJohn Marino
5818*86d7f5d3SJohn Marino
5819*86d7f5d3SJohn Marino /* Save a referrer, potentially for passing to hook scripts later.
5820*86d7f5d3SJohn Marino *
5821*86d7f5d3SJohn Marino * GLOBALS
5822*86d7f5d3SJohn Marino * referrer Where we save the parsed referrer.
5823*86d7f5d3SJohn Marino *
5824*86d7f5d3SJohn Marino * ASSUMPTIONS
5825*86d7f5d3SJohn Marino * The `Root' request has already been processed.
5826*86d7f5d3SJohn Marino * There is no need to dispose of REFERRER if it is set. It's memory is
5827*86d7f5d3SJohn Marino * tracked by parse_root().
5828*86d7f5d3SJohn Marino *
5829*86d7f5d3SJohn Marino * RETURNS
5830*86d7f5d3SJohn Marino * Nothing.
5831*86d7f5d3SJohn Marino */
5832*86d7f5d3SJohn Marino static void
serve_referrer(char * arg)5833*86d7f5d3SJohn Marino serve_referrer (char *arg)
5834*86d7f5d3SJohn Marino {
5835*86d7f5d3SJohn Marino if (error_pending ()) return;
5836*86d7f5d3SJohn Marino
5837*86d7f5d3SJohn Marino referrer = parse_cvsroot (arg);
5838*86d7f5d3SJohn Marino
5839*86d7f5d3SJohn Marino if (!referrer
5840*86d7f5d3SJohn Marino && alloc_pending (80 + strlen (arg)))
5841*86d7f5d3SJohn Marino sprintf (pending_error_text,
5842*86d7f5d3SJohn Marino "E Protocol error: Invalid Referrer: `%s'",
5843*86d7f5d3SJohn Marino arg);
5844*86d7f5d3SJohn Marino }
5845*86d7f5d3SJohn Marino
5846*86d7f5d3SJohn Marino
5847*86d7f5d3SJohn Marino
5848*86d7f5d3SJohn Marino static void serve_valid_requests (char *arg);
5849*86d7f5d3SJohn Marino
5850*86d7f5d3SJohn Marino #endif /* SERVER_SUPPORT */
5851*86d7f5d3SJohn Marino /*
5852*86d7f5d3SJohn Marino * Comment to move position of the following #if line which works
5853*86d7f5d3SJohn Marino * around an apparent bug in Microsoft Visual C++ 6.0 compiler.
5854*86d7f5d3SJohn Marino */
5855*86d7f5d3SJohn Marino #if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
5856*86d7f5d3SJohn Marino /*
5857*86d7f5d3SJohn Marino * Parts of this table are shared with the client code,
5858*86d7f5d3SJohn Marino * but the client doesn't need to know about the handler
5859*86d7f5d3SJohn Marino * functions.
5860*86d7f5d3SJohn Marino */
5861*86d7f5d3SJohn Marino
5862*86d7f5d3SJohn Marino struct request requests[] =
5863*86d7f5d3SJohn Marino {
5864*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
5865*86d7f5d3SJohn Marino #define REQ_LINE(n, f, s) {n, f, s}
5866*86d7f5d3SJohn Marino #else
5867*86d7f5d3SJohn Marino #define REQ_LINE(n, f, s) {n, s}
5868*86d7f5d3SJohn Marino #endif
5869*86d7f5d3SJohn Marino
5870*86d7f5d3SJohn Marino REQ_LINE("Root", serve_root, RQ_ESSENTIAL | RQ_ROOTLESS),
5871*86d7f5d3SJohn Marino REQ_LINE("Valid-responses", serve_valid_responses,
5872*86d7f5d3SJohn Marino RQ_ESSENTIAL | RQ_ROOTLESS),
5873*86d7f5d3SJohn Marino REQ_LINE("valid-requests", serve_valid_requests,
5874*86d7f5d3SJohn Marino RQ_ESSENTIAL | RQ_ROOTLESS),
5875*86d7f5d3SJohn Marino REQ_LINE("Command-prep", serve_command_prep, 0),
5876*86d7f5d3SJohn Marino REQ_LINE("Referrer", serve_referrer, 0),
5877*86d7f5d3SJohn Marino REQ_LINE("Repository", serve_repository, 0),
5878*86d7f5d3SJohn Marino REQ_LINE("Directory", serve_directory, RQ_ESSENTIAL),
5879*86d7f5d3SJohn Marino REQ_LINE("Relative-directory", serve_directory, 0),
5880*86d7f5d3SJohn Marino REQ_LINE("Max-dotdot", serve_max_dotdot, 0),
5881*86d7f5d3SJohn Marino REQ_LINE("Static-directory", serve_static_directory, 0),
5882*86d7f5d3SJohn Marino REQ_LINE("Sticky", serve_sticky, 0),
5883*86d7f5d3SJohn Marino REQ_LINE("Entry", serve_entry, RQ_ESSENTIAL),
5884*86d7f5d3SJohn Marino REQ_LINE("Kopt", serve_kopt, 0),
5885*86d7f5d3SJohn Marino REQ_LINE("Checkin-time", serve_checkin_time, 0),
5886*86d7f5d3SJohn Marino REQ_LINE("Modified", serve_modified, RQ_ESSENTIAL),
5887*86d7f5d3SJohn Marino REQ_LINE("Is-modified", serve_is_modified, 0),
5888*86d7f5d3SJohn Marino
5889*86d7f5d3SJohn Marino /* The client must send this request to interoperate with CVS 1.5
5890*86d7f5d3SJohn Marino through 1.9 servers. The server must support it (although it can
5891*86d7f5d3SJohn Marino be and is a noop) to interoperate with CVS 1.5 to 1.9 clients. */
5892*86d7f5d3SJohn Marino REQ_LINE("UseUnchanged", serve_enable_unchanged, RQ_ENABLEME | RQ_ROOTLESS),
5893*86d7f5d3SJohn Marino
5894*86d7f5d3SJohn Marino REQ_LINE("Unchanged", serve_unchanged, RQ_ESSENTIAL),
5895*86d7f5d3SJohn Marino REQ_LINE("Notify", serve_notify, 0),
5896*86d7f5d3SJohn Marino REQ_LINE("Hostname", serve_hostname, 0),
5897*86d7f5d3SJohn Marino REQ_LINE("LocalDir", serve_localdir, 0),
5898*86d7f5d3SJohn Marino REQ_LINE("Questionable", serve_questionable, 0),
5899*86d7f5d3SJohn Marino REQ_LINE("Argument", serve_argument, RQ_ESSENTIAL),
5900*86d7f5d3SJohn Marino REQ_LINE("Argumentx", serve_argumentx, RQ_ESSENTIAL),
5901*86d7f5d3SJohn Marino REQ_LINE("Global_option", serve_global_option, RQ_ROOTLESS),
5902*86d7f5d3SJohn Marino /* This is rootless, even though the client/server spec does not specify
5903*86d7f5d3SJohn Marino * such, to allow error messages to be understood by the client when they are
5904*86d7f5d3SJohn Marino * sent.
5905*86d7f5d3SJohn Marino */
5906*86d7f5d3SJohn Marino REQ_LINE("Gzip-stream", serve_gzip_stream, RQ_ROOTLESS),
5907*86d7f5d3SJohn Marino REQ_LINE("wrapper-sendme-rcsOptions",
5908*86d7f5d3SJohn Marino serve_wrapper_sendme_rcs_options,
5909*86d7f5d3SJohn Marino 0),
5910*86d7f5d3SJohn Marino REQ_LINE("Set", serve_set, RQ_ROOTLESS),
5911*86d7f5d3SJohn Marino #ifdef ENCRYPTION
5912*86d7f5d3SJohn Marino /* These are rootless despite what the client/server spec says for the same
5913*86d7f5d3SJohn Marino * reasons as Gzip-stream.
5914*86d7f5d3SJohn Marino */
5915*86d7f5d3SJohn Marino # ifdef HAVE_KERBEROS
5916*86d7f5d3SJohn Marino REQ_LINE("Kerberos-encrypt", serve_kerberos_encrypt, RQ_ROOTLESS),
5917*86d7f5d3SJohn Marino # endif
5918*86d7f5d3SJohn Marino # ifdef HAVE_GSSAPI
5919*86d7f5d3SJohn Marino REQ_LINE("Gssapi-encrypt", serve_gssapi_encrypt, RQ_ROOTLESS),
5920*86d7f5d3SJohn Marino # endif
5921*86d7f5d3SJohn Marino #endif
5922*86d7f5d3SJohn Marino #ifdef HAVE_GSSAPI
5923*86d7f5d3SJohn Marino REQ_LINE("Gssapi-authenticate", serve_gssapi_authenticate, RQ_ROOTLESS),
5924*86d7f5d3SJohn Marino #endif
5925*86d7f5d3SJohn Marino REQ_LINE("expand-modules", serve_expand_modules, 0),
5926*86d7f5d3SJohn Marino REQ_LINE("ci", serve_ci, RQ_ESSENTIAL),
5927*86d7f5d3SJohn Marino REQ_LINE("co", serve_co, RQ_ESSENTIAL),
5928*86d7f5d3SJohn Marino REQ_LINE("update", serve_update, RQ_ESSENTIAL),
5929*86d7f5d3SJohn Marino REQ_LINE("diff", serve_diff, 0),
5930*86d7f5d3SJohn Marino REQ_LINE("log", serve_log, 0),
5931*86d7f5d3SJohn Marino REQ_LINE("rlog", serve_rlog, 0),
5932*86d7f5d3SJohn Marino REQ_LINE("list", serve_ls, 0),
5933*86d7f5d3SJohn Marino REQ_LINE("rlist", serve_rls, 0),
5934*86d7f5d3SJohn Marino /* This allows us to avoid sending `-q' as a command argument to `cvs ls',
5935*86d7f5d3SJohn Marino * or more accurately, allows us to send `-q' to backwards CVSNT servers.
5936*86d7f5d3SJohn Marino */
5937*86d7f5d3SJohn Marino REQ_LINE("global-list-quiet", serve_noop, RQ_ROOTLESS),
5938*86d7f5d3SJohn Marino /* Deprecated synonym for rlist, for compatibility with CVSNT. */
5939*86d7f5d3SJohn Marino REQ_LINE("ls", serve_rls, 0),
5940*86d7f5d3SJohn Marino REQ_LINE("add", serve_add, 0),
5941*86d7f5d3SJohn Marino REQ_LINE("remove", serve_remove, 0),
5942*86d7f5d3SJohn Marino REQ_LINE("update-patches", serve_ignore, 0),
5943*86d7f5d3SJohn Marino REQ_LINE("gzip-file-contents", serve_gzip_contents, RQ_ROOTLESS),
5944*86d7f5d3SJohn Marino REQ_LINE("status", serve_status, 0),
5945*86d7f5d3SJohn Marino REQ_LINE("rdiff", serve_rdiff, 0),
5946*86d7f5d3SJohn Marino REQ_LINE("tag", serve_tag, 0),
5947*86d7f5d3SJohn Marino REQ_LINE("rtag", serve_rtag, 0),
5948*86d7f5d3SJohn Marino REQ_LINE("import", serve_import, 0),
5949*86d7f5d3SJohn Marino REQ_LINE("admin", serve_admin, 0),
5950*86d7f5d3SJohn Marino REQ_LINE("export", serve_export, 0),
5951*86d7f5d3SJohn Marino REQ_LINE("history", serve_history, 0),
5952*86d7f5d3SJohn Marino REQ_LINE("release", serve_release, 0),
5953*86d7f5d3SJohn Marino REQ_LINE("watch-on", serve_watch_on, 0),
5954*86d7f5d3SJohn Marino REQ_LINE("watch-off", serve_watch_off, 0),
5955*86d7f5d3SJohn Marino REQ_LINE("watch-add", serve_watch_add, 0),
5956*86d7f5d3SJohn Marino REQ_LINE("watch-remove", serve_watch_remove, 0),
5957*86d7f5d3SJohn Marino REQ_LINE("watchers", serve_watchers, 0),
5958*86d7f5d3SJohn Marino REQ_LINE("editors", serve_editors, 0),
5959*86d7f5d3SJohn Marino REQ_LINE("edit", serve_edit, 0),
5960*86d7f5d3SJohn Marino REQ_LINE("init", serve_init, RQ_ROOTLESS),
5961*86d7f5d3SJohn Marino REQ_LINE("annotate", serve_annotate, 0),
5962*86d7f5d3SJohn Marino REQ_LINE("rannotate", serve_rannotate, 0),
5963*86d7f5d3SJohn Marino REQ_LINE("noop", serve_noop, RQ_ROOTLESS),
5964*86d7f5d3SJohn Marino REQ_LINE("version", serve_version, RQ_ROOTLESS),
5965*86d7f5d3SJohn Marino REQ_LINE(NULL, NULL, 0)
5966*86d7f5d3SJohn Marino
5967*86d7f5d3SJohn Marino #undef REQ_LINE
5968*86d7f5d3SJohn Marino };
5969*86d7f5d3SJohn Marino #endif /* SERVER_SUPPORT or CLIENT_SUPPORT */
5970*86d7f5d3SJohn Marino
5971*86d7f5d3SJohn Marino
5972*86d7f5d3SJohn Marino
5973*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
5974*86d7f5d3SJohn Marino /*
5975*86d7f5d3SJohn Marino * This server request is not ignored by the secondary.
5976*86d7f5d3SJohn Marino */
5977*86d7f5d3SJohn Marino static void
serve_valid_requests(char * arg)5978*86d7f5d3SJohn Marino serve_valid_requests (char *arg)
5979*86d7f5d3SJohn Marino {
5980*86d7f5d3SJohn Marino struct request *rq;
5981*86d7f5d3SJohn Marino
5982*86d7f5d3SJohn Marino /* Since this is processed in the first pass, don't reprocess it in the
5983*86d7f5d3SJohn Marino * second.
5984*86d7f5d3SJohn Marino *
5985*86d7f5d3SJohn Marino * We still print errors since new errors could have been generated in the
5986*86d7f5d3SJohn Marino * second pass.
5987*86d7f5d3SJohn Marino */
5988*86d7f5d3SJohn Marino if (print_pending_error ()
5989*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
5990*86d7f5d3SJohn Marino || reprocessing
5991*86d7f5d3SJohn Marino #endif /* PROXY_SUPPORT */
5992*86d7f5d3SJohn Marino )
5993*86d7f5d3SJohn Marino return;
5994*86d7f5d3SJohn Marino
5995*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "Valid-requests");
5996*86d7f5d3SJohn Marino for (rq = requests; rq->name != NULL; rq++)
5997*86d7f5d3SJohn Marino {
5998*86d7f5d3SJohn Marino if (rq->func != NULL)
5999*86d7f5d3SJohn Marino {
6000*86d7f5d3SJohn Marino buf_append_char (buf_to_net, ' ');
6001*86d7f5d3SJohn Marino buf_output0 (buf_to_net, rq->name);
6002*86d7f5d3SJohn Marino }
6003*86d7f5d3SJohn Marino }
6004*86d7f5d3SJohn Marino
6005*86d7f5d3SJohn Marino if (config && config->MinCompressionLevel
6006*86d7f5d3SJohn Marino && supported_response ("Force-gzip"))
6007*86d7f5d3SJohn Marino {
6008*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "\n");
6009*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "Force-gzip");
6010*86d7f5d3SJohn Marino }
6011*86d7f5d3SJohn Marino
6012*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "\nok\n");
6013*86d7f5d3SJohn Marino
6014*86d7f5d3SJohn Marino /* The client is waiting for the list of valid requests, so we
6015*86d7f5d3SJohn Marino must send the output now. */
6016*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
6017*86d7f5d3SJohn Marino }
6018*86d7f5d3SJohn Marino
6019*86d7f5d3SJohn Marino
6020*86d7f5d3SJohn Marino
6021*86d7f5d3SJohn Marino #ifdef SUNOS_KLUDGE
6022*86d7f5d3SJohn Marino /*
6023*86d7f5d3SJohn Marino * Delete temporary files. SIG is the signal making this happen, or
6024*86d7f5d3SJohn Marino * 0 if not called as a result of a signal.
6025*86d7f5d3SJohn Marino */
6026*86d7f5d3SJohn Marino static int command_pid_is_dead;
wait_sig(int sig)6027*86d7f5d3SJohn Marino static void wait_sig (int sig)
6028*86d7f5d3SJohn Marino {
6029*86d7f5d3SJohn Marino int status;
6030*86d7f5d3SJohn Marino pid_t r = wait (&status);
6031*86d7f5d3SJohn Marino if (r == command_pid)
6032*86d7f5d3SJohn Marino command_pid_is_dead++;
6033*86d7f5d3SJohn Marino }
6034*86d7f5d3SJohn Marino #endif /* SUNOS_KLUDGE */
6035*86d7f5d3SJohn Marino
6036*86d7f5d3SJohn Marino
6037*86d7f5d3SJohn Marino
6038*86d7f5d3SJohn Marino /*
6039*86d7f5d3SJohn Marino * This function cleans up after the server. Specifically, it:
6040*86d7f5d3SJohn Marino *
6041*86d7f5d3SJohn Marino * <ol>
6042*86d7f5d3SJohn Marino * <li>Sets BUF_TO_NET to blocking and fluxhes it.</li>
6043*86d7f5d3SJohn Marino * <li>With SUNOS_KLUDGE enabled:
6044*86d7f5d3SJohn Marino * <ol>
6045*86d7f5d3SJohn Marino * <li>Terminates the command process.</li>
6046*86d7f5d3SJohn Marino * <li>Waits on the command process, draining output as necessary.</li>
6047*86d7f5d3SJohn Marino * </ol>
6048*86d7f5d3SJohn Marino * </li>
6049*86d7f5d3SJohn Marino * <li>Removes the temporary directory.</li>
6050*86d7f5d3SJohn Marino * <li>Flush and shutdown the buffers.</li>
6051*86d7f5d3SJohn Marino * <li>Set ERROR_USE_PROTOCOL and SERVER_ACTIVE to false.</li>
6052*86d7f5d3SJohn Marino * </ol>
6053*86d7f5d3SJohn Marino *
6054*86d7f5d3SJohn Marino * NOTES
6055*86d7f5d3SJohn Marino * This function needs to be reentrant since a call to exit() can cause a
6056*86d7f5d3SJohn Marino * call to this function, which can then be interrupted by a signal, which
6057*86d7f5d3SJohn Marino * can cause a second call to this function.
6058*86d7f5d3SJohn Marino *
6059*86d7f5d3SJohn Marino * GLOBALS
6060*86d7f5d3SJohn Marino * buf_from_net The input buffer which brings data from the
6061*86d7f5d3SJohn Marino * CVS client.
6062*86d7f5d3SJohn Marino * buf_to_net The output buffer which moves data to the CVS
6063*86d7f5d3SJohn Marino * client.
6064*86d7f5d3SJohn Marino * error_use_protocol Set when the server parent process is active.
6065*86d7f5d3SJohn Marino * Cleared for the server child processes.
6066*86d7f5d3SJohn Marino * dont_delete_temp Set when a core dump of a child process is
6067*86d7f5d3SJohn Marino * detected so that the core and related data may
6068*86d7f5d3SJohn Marino * be preserved.
6069*86d7f5d3SJohn Marino * noexec Whether we are supposed to change the disk.
6070*86d7f5d3SJohn Marino * orig_server_temp_dir The temporary directory we created within
6071*86d7f5d3SJohn Marino * Tmpdir for our duplicate of the client
6072*86d7f5d3SJohn Marino * workspace.
6073*86d7f5d3SJohn Marino *
6074*86d7f5d3SJohn Marino * INPUTS
6075*86d7f5d3SJohn Marino * None.
6076*86d7f5d3SJohn Marino *
6077*86d7f5d3SJohn Marino * ERRORS
6078*86d7f5d3SJohn Marino * Problems encountered during the cleanup, for instance low memory or
6079*86d7f5d3SJohn Marino * problems deleting the temp files and directories, can cause the error
6080*86d7f5d3SJohn Marino * function to be called, which might call exit. If exit gets called in this
6081*86d7f5d3SJohn Marino * manner. this routine will not complete, but the other exit handlers
6082*86d7f5d3SJohn Marino * registered via atexit() will still run.
6083*86d7f5d3SJohn Marino *
6084*86d7f5d3SJohn Marino * RETURNS
6085*86d7f5d3SJohn Marino * Nothing.
6086*86d7f5d3SJohn Marino */
6087*86d7f5d3SJohn Marino void
server_cleanup(void)6088*86d7f5d3SJohn Marino server_cleanup (void)
6089*86d7f5d3SJohn Marino {
6090*86d7f5d3SJohn Marino TRACE (TRACE_FUNCTION, "server_cleanup()");
6091*86d7f5d3SJohn Marino
6092*86d7f5d3SJohn Marino assert (server_active);
6093*86d7f5d3SJohn Marino
6094*86d7f5d3SJohn Marino /* FIXME: Do not perform buffered I/O from an interrupt handler like
6095*86d7f5d3SJohn Marino * this (via error). However, I'm leaving the error-calling code there
6096*86d7f5d3SJohn Marino * in the hope that on the rare occasion the error call is actually made
6097*86d7f5d3SJohn Marino * (e.g., a fluky I/O error or permissions problem prevents the deletion
6098*86d7f5d3SJohn Marino * of a just-created file) reentrancy won't be an issue.
6099*86d7f5d3SJohn Marino */
6100*86d7f5d3SJohn Marino
6101*86d7f5d3SJohn Marino /* We don't want to be interrupted during calls which set globals to NULL,
6102*86d7f5d3SJohn Marino * but we know that by the time we reach this function, interrupts have
6103*86d7f5d3SJohn Marino * already been blocked.
6104*86d7f5d3SJohn Marino */
6105*86d7f5d3SJohn Marino
6106*86d7f5d3SJohn Marino /* Since we install this function in an atexit() handler before forking,
6107*86d7f5d3SJohn Marino * reuse the ERROR_USE_PROTOCOL flag, which we know is only set in the
6108*86d7f5d3SJohn Marino * parent server process, to avoid cleaning up the temp space multiple
6109*86d7f5d3SJohn Marino * times. Skip the buf_to_net checks too as an optimization since we know
6110*86d7f5d3SJohn Marino * they will be set to NULL in the child process anyhow.
6111*86d7f5d3SJohn Marino */
6112*86d7f5d3SJohn Marino if (error_use_protocol)
6113*86d7f5d3SJohn Marino {
6114*86d7f5d3SJohn Marino if (buf_to_net != NULL)
6115*86d7f5d3SJohn Marino {
6116*86d7f5d3SJohn Marino int status;
6117*86d7f5d3SJohn Marino
6118*86d7f5d3SJohn Marino /* Since we're done, go ahead and put BUF_TO_NET back into blocking
6119*86d7f5d3SJohn Marino * mode and send any pending output. In the usual case there won't
6120*86d7f5d3SJohn Marino * won't be any, but there might be if an error occured.
6121*86d7f5d3SJohn Marino */
6122*86d7f5d3SJohn Marino
6123*86d7f5d3SJohn Marino set_block (buf_to_net);
6124*86d7f5d3SJohn Marino buf_flush (buf_to_net, 1);
6125*86d7f5d3SJohn Marino
6126*86d7f5d3SJohn Marino /* Next we shut down BUF_FROM_NET. That will pick up the checksum
6127*86d7f5d3SJohn Marino * generated when the client shuts down its buffer. Then, after we
6128*86d7f5d3SJohn Marino * have generated any final output, we shut down BUF_TO_NET.
6129*86d7f5d3SJohn Marino */
6130*86d7f5d3SJohn Marino
6131*86d7f5d3SJohn Marino /* SIG_beginCrSect(); */
6132*86d7f5d3SJohn Marino if (buf_from_net)
6133*86d7f5d3SJohn Marino {
6134*86d7f5d3SJohn Marino status = buf_shutdown (buf_from_net);
6135*86d7f5d3SJohn Marino if (status != 0)
6136*86d7f5d3SJohn Marino error (0, status, "shutting down buffer from client");
6137*86d7f5d3SJohn Marino buf_free (buf_from_net);
6138*86d7f5d3SJohn Marino buf_from_net = NULL;
6139*86d7f5d3SJohn Marino }
6140*86d7f5d3SJohn Marino /* SIG_endCrSect(); */
6141*86d7f5d3SJohn Marino }
6142*86d7f5d3SJohn Marino
6143*86d7f5d3SJohn Marino if (!dont_delete_temp)
6144*86d7f5d3SJohn Marino {
6145*86d7f5d3SJohn Marino int save_noexec;
6146*86d7f5d3SJohn Marino
6147*86d7f5d3SJohn Marino /* What a bogus kludge. This disgusting code makes all kinds of
6148*86d7f5d3SJohn Marino assumptions about SunOS, and is only for a bug in that system.
6149*86d7f5d3SJohn Marino So only enable it on Suns. */
6150*86d7f5d3SJohn Marino #ifdef SUNOS_KLUDGE
6151*86d7f5d3SJohn Marino if (command_pid > 0)
6152*86d7f5d3SJohn Marino {
6153*86d7f5d3SJohn Marino /* To avoid crashes on SunOS due to bugs in SunOS tmpfs
6154*86d7f5d3SJohn Marino * triggered by the use of rename() in RCS, wait for the
6155*86d7f5d3SJohn Marino * subprocess to die. Unfortunately, this means draining
6156*86d7f5d3SJohn Marino * output while waiting for it to unblock the signal we sent
6157*86d7f5d3SJohn Marino * it. Yuck!
6158*86d7f5d3SJohn Marino */
6159*86d7f5d3SJohn Marino int status;
6160*86d7f5d3SJohn Marino pid_t r;
6161*86d7f5d3SJohn Marino
6162*86d7f5d3SJohn Marino signal (SIGCHLD, wait_sig);
6163*86d7f5d3SJohn Marino /* Perhaps SIGTERM would be more correct. But the child
6164*86d7f5d3SJohn Marino process will delay the SIGINT delivery until its own
6165*86d7f5d3SJohn Marino children have exited. */
6166*86d7f5d3SJohn Marino kill (command_pid, SIGINT);
6167*86d7f5d3SJohn Marino /* The caller may also have sent a signal to command_pid, so
6168*86d7f5d3SJohn Marino * always try waiting. First, though, check and see if it's
6169*86d7f5d3SJohn Marino * still there....
6170*86d7f5d3SJohn Marino */
6171*86d7f5d3SJohn Marino do_waitpid:
6172*86d7f5d3SJohn Marino r = waitpid (command_pid, &status, WNOHANG);
6173*86d7f5d3SJohn Marino if (r == 0)
6174*86d7f5d3SJohn Marino ;
6175*86d7f5d3SJohn Marino else if (r == command_pid)
6176*86d7f5d3SJohn Marino command_pid_is_dead++;
6177*86d7f5d3SJohn Marino else if (r == -1)
6178*86d7f5d3SJohn Marino switch (errno)
6179*86d7f5d3SJohn Marino {
6180*86d7f5d3SJohn Marino case ECHILD:
6181*86d7f5d3SJohn Marino command_pid_is_dead++;
6182*86d7f5d3SJohn Marino break;
6183*86d7f5d3SJohn Marino case EINTR:
6184*86d7f5d3SJohn Marino goto do_waitpid;
6185*86d7f5d3SJohn Marino }
6186*86d7f5d3SJohn Marino else
6187*86d7f5d3SJohn Marino /* waitpid should always return one of the above values */
6188*86d7f5d3SJohn Marino abort ();
6189*86d7f5d3SJohn Marino while (!command_pid_is_dead)
6190*86d7f5d3SJohn Marino {
6191*86d7f5d3SJohn Marino struct timeval timeout;
6192*86d7f5d3SJohn Marino struct fd_set_wrapper readfds;
6193*86d7f5d3SJohn Marino char buf[100];
6194*86d7f5d3SJohn Marino int i;
6195*86d7f5d3SJohn Marino
6196*86d7f5d3SJohn Marino /* Use a non-zero timeout to avoid eating up CPU cycles. */
6197*86d7f5d3SJohn Marino timeout.tv_sec = 2;
6198*86d7f5d3SJohn Marino timeout.tv_usec = 0;
6199*86d7f5d3SJohn Marino readfds = command_fds_to_drain;
6200*86d7f5d3SJohn Marino switch (select (max_command_fd + 1, &readfds.fds,
6201*86d7f5d3SJohn Marino NULL, NULL &timeout))
6202*86d7f5d3SJohn Marino {
6203*86d7f5d3SJohn Marino case -1:
6204*86d7f5d3SJohn Marino if (errno != EINTR)
6205*86d7f5d3SJohn Marino abort ();
6206*86d7f5d3SJohn Marino case 0:
6207*86d7f5d3SJohn Marino /* timeout */
6208*86d7f5d3SJohn Marino break;
6209*86d7f5d3SJohn Marino case 1:
6210*86d7f5d3SJohn Marino for (i = 0; i <= max_command_fd; i++)
6211*86d7f5d3SJohn Marino {
6212*86d7f5d3SJohn Marino if (!FD_ISSET (i, &readfds.fds))
6213*86d7f5d3SJohn Marino continue;
6214*86d7f5d3SJohn Marino /* this fd is non-blocking */
6215*86d7f5d3SJohn Marino while (read (i, buf, sizeof (buf)) >= 1)
6216*86d7f5d3SJohn Marino ;
6217*86d7f5d3SJohn Marino }
6218*86d7f5d3SJohn Marino break;
6219*86d7f5d3SJohn Marino default:
6220*86d7f5d3SJohn Marino abort ();
6221*86d7f5d3SJohn Marino }
6222*86d7f5d3SJohn Marino }
6223*86d7f5d3SJohn Marino }
6224*86d7f5d3SJohn Marino #endif /* SUNOS_KLUDGE */
6225*86d7f5d3SJohn Marino
6226*86d7f5d3SJohn Marino /* Make sure our working directory isn't inside the tree we're
6227*86d7f5d3SJohn Marino going to delete. */
6228*86d7f5d3SJohn Marino CVS_CHDIR (get_cvs_tmp_dir ());
6229*86d7f5d3SJohn Marino
6230*86d7f5d3SJohn Marino /* Temporarily clear noexec, so that we clean up our temp directory
6231*86d7f5d3SJohn Marino regardless of it (this could more cleanly be handled by moving
6232*86d7f5d3SJohn Marino the noexec check to all the unlink_file_dir callers from
6233*86d7f5d3SJohn Marino unlink_file_dir itself). */
6234*86d7f5d3SJohn Marino save_noexec = noexec;
6235*86d7f5d3SJohn Marino
6236*86d7f5d3SJohn Marino /* SIG_beginCrSect(); */
6237*86d7f5d3SJohn Marino noexec = 0;
6238*86d7f5d3SJohn Marino unlink_file_dir (orig_server_temp_dir);
6239*86d7f5d3SJohn Marino noexec = save_noexec;
6240*86d7f5d3SJohn Marino /* SIG_endCrSect(); */
6241*86d7f5d3SJohn Marino } /* !dont_delete_temp */
6242*86d7f5d3SJohn Marino
6243*86d7f5d3SJohn Marino /* SIG_beginCrSect(); */
6244*86d7f5d3SJohn Marino if (buf_to_net != NULL)
6245*86d7f5d3SJohn Marino {
6246*86d7f5d3SJohn Marino /* Save BUF_TO_NET and set the global pointer to NULL so that any
6247*86d7f5d3SJohn Marino * error messages generated during shutdown go to the syslog rather
6248*86d7f5d3SJohn Marino * than getting lost.
6249*86d7f5d3SJohn Marino */
6250*86d7f5d3SJohn Marino struct buffer *buf_to_net_save = buf_to_net;
6251*86d7f5d3SJohn Marino buf_to_net = NULL;
6252*86d7f5d3SJohn Marino
6253*86d7f5d3SJohn Marino (void) buf_flush (buf_to_net_save, 1);
6254*86d7f5d3SJohn Marino (void) buf_shutdown (buf_to_net_save);
6255*86d7f5d3SJohn Marino buf_free (buf_to_net_save);
6256*86d7f5d3SJohn Marino error_use_protocol = 0;
6257*86d7f5d3SJohn Marino }
6258*86d7f5d3SJohn Marino /* SIG_endCrSect(); */
6259*86d7f5d3SJohn Marino }
6260*86d7f5d3SJohn Marino
6261*86d7f5d3SJohn Marino server_active = 0;
6262*86d7f5d3SJohn Marino }
6263*86d7f5d3SJohn Marino
6264*86d7f5d3SJohn Marino
6265*86d7f5d3SJohn Marino
6266*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
6267*86d7f5d3SJohn Marino size_t MaxProxyBufferSize = (size_t)(8 * 1024 * 1024); /* 8 megabytes,
6268*86d7f5d3SJohn Marino * by default.
6269*86d7f5d3SJohn Marino */
6270*86d7f5d3SJohn Marino #endif /* PROXY_SUPPORT */
6271*86d7f5d3SJohn Marino
6272*86d7f5d3SJohn Marino static const char *const server_usage[] =
6273*86d7f5d3SJohn Marino {
6274*86d7f5d3SJohn Marino "Usage: %s %s [-c config-file]\n",
6275*86d7f5d3SJohn Marino "\t-c config-file\tPath to an alternative CVS config file.\n",
6276*86d7f5d3SJohn Marino "Normally invoked by a cvs client on a remote machine.\n",
6277*86d7f5d3SJohn Marino NULL
6278*86d7f5d3SJohn Marino };
6279*86d7f5d3SJohn Marino
6280*86d7f5d3SJohn Marino
6281*86d7f5d3SJohn Marino
6282*86d7f5d3SJohn Marino void
parseServerOptions(int argc,char ** argv)6283*86d7f5d3SJohn Marino parseServerOptions (int argc, char **argv)
6284*86d7f5d3SJohn Marino {
6285*86d7f5d3SJohn Marino int c;
6286*86d7f5d3SJohn Marino
6287*86d7f5d3SJohn Marino optind = 0;
6288*86d7f5d3SJohn Marino while ((c = getopt (argc, argv, "+c:")) != -1)
6289*86d7f5d3SJohn Marino {
6290*86d7f5d3SJohn Marino switch (c)
6291*86d7f5d3SJohn Marino {
6292*86d7f5d3SJohn Marino #ifdef ALLOW_CONFIG_OVERRIDE
6293*86d7f5d3SJohn Marino case 'c':
6294*86d7f5d3SJohn Marino if (gConfigPath) free (gConfigPath);
6295*86d7f5d3SJohn Marino gConfigPath = xstrdup (optarg);
6296*86d7f5d3SJohn Marino break;
6297*86d7f5d3SJohn Marino #endif
6298*86d7f5d3SJohn Marino case '?':
6299*86d7f5d3SJohn Marino default:
6300*86d7f5d3SJohn Marino usage (server_usage);
6301*86d7f5d3SJohn Marino break;
6302*86d7f5d3SJohn Marino }
6303*86d7f5d3SJohn Marino }
6304*86d7f5d3SJohn Marino }
6305*86d7f5d3SJohn Marino
6306*86d7f5d3SJohn Marino
6307*86d7f5d3SJohn Marino
6308*86d7f5d3SJohn Marino int
server(int argc,char ** argv)6309*86d7f5d3SJohn Marino server (int argc, char **argv)
6310*86d7f5d3SJohn Marino {
6311*86d7f5d3SJohn Marino char *error_prog_name; /* Used in error messages */
6312*86d7f5d3SJohn Marino
6313*86d7f5d3SJohn Marino if (argc == -1)
6314*86d7f5d3SJohn Marino usage (server_usage);
6315*86d7f5d3SJohn Marino
6316*86d7f5d3SJohn Marino /* Options were pre-parsed in main.c. */
6317*86d7f5d3SJohn Marino
6318*86d7f5d3SJohn Marino /*
6319*86d7f5d3SJohn Marino * Set this in .bashrc if you want to give yourself time to attach
6320*86d7f5d3SJohn Marino * to the subprocess with a debugger.
6321*86d7f5d3SJohn Marino */
6322*86d7f5d3SJohn Marino if (getenv ("CVS_PARENT_SERVER_SLEEP"))
6323*86d7f5d3SJohn Marino {
6324*86d7f5d3SJohn Marino int secs = atoi (getenv ("CVS_PARENT_SERVER_SLEEP"));
6325*86d7f5d3SJohn Marino TRACE (TRACE_DATA, "Sleeping CVS_PARENT_SERVER_SLEEP (%d) seconds",
6326*86d7f5d3SJohn Marino secs);
6327*86d7f5d3SJohn Marino sleep (secs);
6328*86d7f5d3SJohn Marino }
6329*86d7f5d3SJohn Marino else
6330*86d7f5d3SJohn Marino TRACE (TRACE_DATA, "CVS_PARENT_SERVER_SLEEP not set.");
6331*86d7f5d3SJohn Marino
6332*86d7f5d3SJohn Marino /* pserver_authenticate_connection () (called from main ()) can initialize
6333*86d7f5d3SJohn Marino * these.
6334*86d7f5d3SJohn Marino */
6335*86d7f5d3SJohn Marino if (!buf_to_net)
6336*86d7f5d3SJohn Marino {
6337*86d7f5d3SJohn Marino buf_to_net = fd_buffer_initialize (STDOUT_FILENO, 0, NULL, false,
6338*86d7f5d3SJohn Marino outbuf_memory_error);
6339*86d7f5d3SJohn Marino buf_from_net = fd_buffer_initialize (STDIN_FILENO, 0, NULL, true,
6340*86d7f5d3SJohn Marino outbuf_memory_error);
6341*86d7f5d3SJohn Marino }
6342*86d7f5d3SJohn Marino
6343*86d7f5d3SJohn Marino setup_logfiles ("CVS_SERVER_LOG", &buf_to_net, &buf_from_net);
6344*86d7f5d3SJohn Marino
6345*86d7f5d3SJohn Marino #ifdef PROXY_SUPPORT
6346*86d7f5d3SJohn Marino /* We have to set up the recording for all servers. Until we receive the
6347*86d7f5d3SJohn Marino * `Root' request and load CVSROOT/config, we can't tell if we are a
6348*86d7f5d3SJohn Marino * secondary or primary.
6349*86d7f5d3SJohn Marino */
6350*86d7f5d3SJohn Marino {
6351*86d7f5d3SJohn Marino /* Open the secondary log. */
6352*86d7f5d3SJohn Marino buf_from_net = log_buffer_initialize (buf_from_net, NULL,
6353*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
6354*86d7f5d3SJohn Marino true,
6355*86d7f5d3SJohn Marino config
6356*86d7f5d3SJohn Marino ? config->MaxProxyBufferSize
6357*86d7f5d3SJohn Marino : MaxProxyBufferSize,
6358*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
6359*86d7f5d3SJohn Marino true, outbuf_memory_error);
6360*86d7f5d3SJohn Marino proxy_log = buf_from_net;
6361*86d7f5d3SJohn Marino
6362*86d7f5d3SJohn Marino /* And again for the out log. */
6363*86d7f5d3SJohn Marino buf_to_net = log_buffer_initialize (buf_to_net, NULL,
6364*86d7f5d3SJohn Marino # ifdef PROXY_SUPPORT
6365*86d7f5d3SJohn Marino true,
6366*86d7f5d3SJohn Marino config
6367*86d7f5d3SJohn Marino ? config->MaxProxyBufferSize
6368*86d7f5d3SJohn Marino : MaxProxyBufferSize,
6369*86d7f5d3SJohn Marino # endif /* PROXY_SUPPORT */
6370*86d7f5d3SJohn Marino false, outbuf_memory_error);
6371*86d7f5d3SJohn Marino proxy_log_out = buf_to_net;
6372*86d7f5d3SJohn Marino }
6373*86d7f5d3SJohn Marino #endif /* PROXY_SUPPORT */
6374*86d7f5d3SJohn Marino
6375*86d7f5d3SJohn Marino saved_output = buf_nonio_initialize (outbuf_memory_error);
6376*86d7f5d3SJohn Marino saved_outerr = buf_nonio_initialize (outbuf_memory_error);
6377*86d7f5d3SJohn Marino
6378*86d7f5d3SJohn Marino /* Since we're in the server parent process, error should use the
6379*86d7f5d3SJohn Marino protocol to report error messages. */
6380*86d7f5d3SJohn Marino error_use_protocol = 1;
6381*86d7f5d3SJohn Marino
6382*86d7f5d3SJohn Marino /* Now initialize our argument vector (for arguments from the client). */
6383*86d7f5d3SJohn Marino
6384*86d7f5d3SJohn Marino /* Small for testing. */
6385*86d7f5d3SJohn Marino argument_vector_size = 1;
6386*86d7f5d3SJohn Marino argument_vector = xmalloc (argument_vector_size * sizeof (char *));
6387*86d7f5d3SJohn Marino argument_count = 1;
6388*86d7f5d3SJohn Marino /* This gets printed if the client supports an option which the
6389*86d7f5d3SJohn Marino server doesn't, causing the server to print a usage message.
6390*86d7f5d3SJohn Marino FIXME: just a nit, I suppose, but the usage message the server
6391*86d7f5d3SJohn Marino prints isn't literally true--it suggests "cvs server" followed
6392*86d7f5d3SJohn Marino by options which are for a particular command. Might be nice to
6393*86d7f5d3SJohn Marino say something like "client apparently supports an option not supported
6394*86d7f5d3SJohn Marino by this server" or something like that instead of usage message. */
6395*86d7f5d3SJohn Marino error_prog_name = xmalloc (strlen (program_name) + 8);
6396*86d7f5d3SJohn Marino sprintf(error_prog_name, "%s server", program_name);
6397*86d7f5d3SJohn Marino argument_vector[0] = error_prog_name;
6398*86d7f5d3SJohn Marino
6399*86d7f5d3SJohn Marino while (1)
6400*86d7f5d3SJohn Marino {
6401*86d7f5d3SJohn Marino char *cmd, *orig_cmd;
6402*86d7f5d3SJohn Marino struct request *rq;
6403*86d7f5d3SJohn Marino int status;
6404*86d7f5d3SJohn Marino
6405*86d7f5d3SJohn Marino status = buf_read_line (buf_from_net, &cmd, NULL);
6406*86d7f5d3SJohn Marino if (status == -2)
6407*86d7f5d3SJohn Marino {
6408*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "E Fatal server error, aborting.\n\
6409*86d7f5d3SJohn Marino error ENOMEM Virtual memory exhausted.\n");
6410*86d7f5d3SJohn Marino break;
6411*86d7f5d3SJohn Marino }
6412*86d7f5d3SJohn Marino if (status != 0)
6413*86d7f5d3SJohn Marino break;
6414*86d7f5d3SJohn Marino
6415*86d7f5d3SJohn Marino orig_cmd = cmd;
6416*86d7f5d3SJohn Marino for (rq = requests; rq->name != NULL; ++rq)
6417*86d7f5d3SJohn Marino if (strncmp (cmd, rq->name, strlen (rq->name)) == 0)
6418*86d7f5d3SJohn Marino {
6419*86d7f5d3SJohn Marino int len = strlen (rq->name);
6420*86d7f5d3SJohn Marino if (cmd[len] == '\0')
6421*86d7f5d3SJohn Marino cmd += len;
6422*86d7f5d3SJohn Marino else if (cmd[len] == ' ')
6423*86d7f5d3SJohn Marino cmd += len + 1;
6424*86d7f5d3SJohn Marino else
6425*86d7f5d3SJohn Marino /*
6426*86d7f5d3SJohn Marino * The first len characters match, but it's a different
6427*86d7f5d3SJohn Marino * command. e.g. the command is "cooperate" but we matched
6428*86d7f5d3SJohn Marino * "co".
6429*86d7f5d3SJohn Marino */
6430*86d7f5d3SJohn Marino continue;
6431*86d7f5d3SJohn Marino
6432*86d7f5d3SJohn Marino if (!(rq->flags & RQ_ROOTLESS)
6433*86d7f5d3SJohn Marino && current_parsed_root == NULL)
6434*86d7f5d3SJohn Marino {
6435*86d7f5d3SJohn Marino if (alloc_pending (80))
6436*86d7f5d3SJohn Marino sprintf (pending_error_text,
6437*86d7f5d3SJohn Marino "E Protocol error: Root request missing");
6438*86d7f5d3SJohn Marino }
6439*86d7f5d3SJohn Marino else
6440*86d7f5d3SJohn Marino {
6441*86d7f5d3SJohn Marino if (config && config->MinCompressionLevel && !gzip_level
6442*86d7f5d3SJohn Marino && !(rq->flags & RQ_ROOTLESS))
6443*86d7f5d3SJohn Marino {
6444*86d7f5d3SJohn Marino /* This is a rootless request, a minimum compression
6445*86d7f5d3SJohn Marino * level has been configured, and no compression has
6446*86d7f5d3SJohn Marino * been requested by the client.
6447*86d7f5d3SJohn Marino */
6448*86d7f5d3SJohn Marino if (alloc_pending (80 + strlen (program_name)))
6449*86d7f5d3SJohn Marino sprintf (pending_error_text,
6450*86d7f5d3SJohn Marino "E %s [server aborted]: Compression must be used with this server.",
6451*86d7f5d3SJohn Marino program_name);
6452*86d7f5d3SJohn Marino }
6453*86d7f5d3SJohn Marino (*rq->func) (cmd);
6454*86d7f5d3SJohn Marino }
6455*86d7f5d3SJohn Marino break;
6456*86d7f5d3SJohn Marino }
6457*86d7f5d3SJohn Marino if (rq->name == NULL)
6458*86d7f5d3SJohn Marino {
6459*86d7f5d3SJohn Marino if (!print_pending_error ())
6460*86d7f5d3SJohn Marino {
6461*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "error unrecognized request `");
6462*86d7f5d3SJohn Marino buf_output0 (buf_to_net, cmd);
6463*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\'');
6464*86d7f5d3SJohn Marino buf_append_char (buf_to_net, '\n');
6465*86d7f5d3SJohn Marino }
6466*86d7f5d3SJohn Marino }
6467*86d7f5d3SJohn Marino free (orig_cmd);
6468*86d7f5d3SJohn Marino }
6469*86d7f5d3SJohn Marino
6470*86d7f5d3SJohn Marino free (error_prog_name);
6471*86d7f5d3SJohn Marino
6472*86d7f5d3SJohn Marino /* We expect the client is done talking to us at this point. If there is
6473*86d7f5d3SJohn Marino * any data in the buffer or on the network pipe, then something we didn't
6474*86d7f5d3SJohn Marino * prepare for is happening.
6475*86d7f5d3SJohn Marino */
6476*86d7f5d3SJohn Marino if (!buf_empty (buf_from_net))
6477*86d7f5d3SJohn Marino {
6478*86d7f5d3SJohn Marino /* Try to send the error message to the client, but also syslog it, in
6479*86d7f5d3SJohn Marino * case the client isn't listening anymore.
6480*86d7f5d3SJohn Marino */
6481*86d7f5d3SJohn Marino #ifdef HAVE_SYSLOG_H
6482*86d7f5d3SJohn Marino /* FIXME: Can the IP address of the connecting client be retrieved
6483*86d7f5d3SJohn Marino * and printed here?
6484*86d7f5d3SJohn Marino */
6485*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR, "Dying gasps received from client.");
6486*86d7f5d3SJohn Marino #endif /* HAVE_SYSLOG_H */
6487*86d7f5d3SJohn Marino error (0, 0, "Dying gasps received from client.");
6488*86d7f5d3SJohn Marino }
6489*86d7f5d3SJohn Marino
6490*86d7f5d3SJohn Marino #ifdef HAVE_PAM
6491*86d7f5d3SJohn Marino if (pamh)
6492*86d7f5d3SJohn Marino {
6493*86d7f5d3SJohn Marino int retval;
6494*86d7f5d3SJohn Marino
6495*86d7f5d3SJohn Marino retval = pam_close_session (pamh, 0);
6496*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
6497*86d7f5d3SJohn Marino if (retval != PAM_SUCCESS)
6498*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR,
6499*86d7f5d3SJohn Marino "PAM close session error: %s",
6500*86d7f5d3SJohn Marino pam_strerror (pamh, retval));
6501*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
6502*86d7f5d3SJohn Marino
6503*86d7f5d3SJohn Marino retval = pam_end (pamh, retval);
6504*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
6505*86d7f5d3SJohn Marino if (retval != PAM_SUCCESS)
6506*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR,
6507*86d7f5d3SJohn Marino "PAM failed to release authenticator, error: %s",
6508*86d7f5d3SJohn Marino pam_strerror (pamh, retval));
6509*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
6510*86d7f5d3SJohn Marino }
6511*86d7f5d3SJohn Marino #endif /* HAVE_PAM */
6512*86d7f5d3SJohn Marino
6513*86d7f5d3SJohn Marino /* server_cleanup() will be called on a normal exit and close the buffers
6514*86d7f5d3SJohn Marino * explicitly.
6515*86d7f5d3SJohn Marino */
6516*86d7f5d3SJohn Marino return 0;
6517*86d7f5d3SJohn Marino }
6518*86d7f5d3SJohn Marino
6519*86d7f5d3SJohn Marino
6520*86d7f5d3SJohn Marino
6521*86d7f5d3SJohn Marino #if defined (HAVE_KERBEROS) || defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
6522*86d7f5d3SJohn Marino static void
switch_to_user(const char * cvs_username,const char * username)6523*86d7f5d3SJohn Marino switch_to_user (const char *cvs_username, const char *username)
6524*86d7f5d3SJohn Marino {
6525*86d7f5d3SJohn Marino struct passwd *pw;
6526*86d7f5d3SJohn Marino #ifdef HAVE_PAM
6527*86d7f5d3SJohn Marino int retval;
6528*86d7f5d3SJohn Marino char *pam_stage = "open session";
6529*86d7f5d3SJohn Marino
6530*86d7f5d3SJohn Marino if (pamh)
6531*86d7f5d3SJohn Marino {
6532*86d7f5d3SJohn Marino retval = pam_open_session (pamh, 0);
6533*86d7f5d3SJohn Marino if (retval == PAM_SUCCESS)
6534*86d7f5d3SJohn Marino {
6535*86d7f5d3SJohn Marino pam_stage = "get pam user";
6536*86d7f5d3SJohn Marino retval = pam_get_item (pamh, PAM_USER, (const void **)&username);
6537*86d7f5d3SJohn Marino }
6538*86d7f5d3SJohn Marino
6539*86d7f5d3SJohn Marino if (retval != PAM_SUCCESS)
6540*86d7f5d3SJohn Marino {
6541*86d7f5d3SJohn Marino printf("E PAM %s error: %s\n", pam_stage,
6542*86d7f5d3SJohn Marino pam_strerror (pamh, retval));
6543*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
6544*86d7f5d3SJohn Marino }
6545*86d7f5d3SJohn Marino }
6546*86d7f5d3SJohn Marino #endif
6547*86d7f5d3SJohn Marino
6548*86d7f5d3SJohn Marino pw = getpwnam (username);
6549*86d7f5d3SJohn Marino if (pw == NULL)
6550*86d7f5d3SJohn Marino {
6551*86d7f5d3SJohn Marino /* check_password contains a similar check, so this usually won't be
6552*86d7f5d3SJohn Marino reached unless the CVS user is mapped to an invalid system user. */
6553*86d7f5d3SJohn Marino
6554*86d7f5d3SJohn Marino printf ("E Fatal error, aborting.\n\
6555*86d7f5d3SJohn Marino error 0 %s: no such system user\n", username);
6556*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
6557*86d7f5d3SJohn Marino }
6558*86d7f5d3SJohn Marino
6559*86d7f5d3SJohn Marino if (pw->pw_uid == 0)
6560*86d7f5d3SJohn Marino {
6561*86d7f5d3SJohn Marino #ifdef HAVE_SYSLOG_H
6562*86d7f5d3SJohn Marino /* FIXME: Can the IP address of the connecting client be retrieved
6563*86d7f5d3SJohn Marino * and printed here?
6564*86d7f5d3SJohn Marino */
6565*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ALERT,
6566*86d7f5d3SJohn Marino "attempt to root from account: %s", cvs_username
6567*86d7f5d3SJohn Marino );
6568*86d7f5d3SJohn Marino #endif /* HAVE_SYSLOG_H */
6569*86d7f5d3SJohn Marino printf("error 0: root not allowed\n");
6570*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
6571*86d7f5d3SJohn Marino }
6572*86d7f5d3SJohn Marino
6573*86d7f5d3SJohn Marino #if HAVE_INITGROUPS
6574*86d7f5d3SJohn Marino if (initgroups (pw->pw_name, pw->pw_gid) < 0
6575*86d7f5d3SJohn Marino # ifdef EPERM
6576*86d7f5d3SJohn Marino /* At least on the system I tried, initgroups() only works as root.
6577*86d7f5d3SJohn Marino But we do still want to report ENOMEM and whatever other
6578*86d7f5d3SJohn Marino errors initgroups() might dish up. */
6579*86d7f5d3SJohn Marino && errno != EPERM
6580*86d7f5d3SJohn Marino # endif
6581*86d7f5d3SJohn Marino )
6582*86d7f5d3SJohn Marino {
6583*86d7f5d3SJohn Marino /* This could be a warning, but I'm not sure I see the point
6584*86d7f5d3SJohn Marino in doing that instead of an error given that it would happen
6585*86d7f5d3SJohn Marino on every connection. We could log it somewhere and not tell
6586*86d7f5d3SJohn Marino the user. But at least for now make it an error. */
6587*86d7f5d3SJohn Marino printf ("error 0 initgroups failed: %s\n", strerror (errno));
6588*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
6589*86d7f5d3SJohn Marino }
6590*86d7f5d3SJohn Marino #endif /* HAVE_INITGROUPS */
6591*86d7f5d3SJohn Marino
6592*86d7f5d3SJohn Marino #ifdef HAVE_PAM
6593*86d7f5d3SJohn Marino if (pamh)
6594*86d7f5d3SJohn Marino {
6595*86d7f5d3SJohn Marino retval = pam_setcred (pamh, PAM_ESTABLISH_CRED);
6596*86d7f5d3SJohn Marino if (retval != PAM_SUCCESS)
6597*86d7f5d3SJohn Marino {
6598*86d7f5d3SJohn Marino printf("E PAM reestablish credentials error: %s\n",
6599*86d7f5d3SJohn Marino pam_strerror (pamh, retval));
6600*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
6601*86d7f5d3SJohn Marino }
6602*86d7f5d3SJohn Marino }
6603*86d7f5d3SJohn Marino #endif
6604*86d7f5d3SJohn Marino
6605*86d7f5d3SJohn Marino #ifdef SETXID_SUPPORT
6606*86d7f5d3SJohn Marino /* honor the setgid bit iff set*/
6607*86d7f5d3SJohn Marino if (getgid() != getegid())
6608*86d7f5d3SJohn Marino {
6609*86d7f5d3SJohn Marino if (setgid (getegid ()) < 0)
6610*86d7f5d3SJohn Marino {
6611*86d7f5d3SJohn Marino /* See comments at setuid call below for more discussion. */
6612*86d7f5d3SJohn Marino printf ("error 0 setgid failed: %s\n", strerror (errno));
6613*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
6614*86d7f5d3SJohn Marino }
6615*86d7f5d3SJohn Marino }
6616*86d7f5d3SJohn Marino else
6617*86d7f5d3SJohn Marino #endif
6618*86d7f5d3SJohn Marino {
6619*86d7f5d3SJohn Marino if (setgid (pw->pw_gid) < 0)
6620*86d7f5d3SJohn Marino {
6621*86d7f5d3SJohn Marino /* See comments at setuid call below for more discussion. */
6622*86d7f5d3SJohn Marino printf ("error 0 setgid failed: %s\n", strerror (errno));
6623*86d7f5d3SJohn Marino #ifdef HAVE_SYSLOG_H
6624*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR,
6625*86d7f5d3SJohn Marino "setgid to %d failed (%m): real %d/%d, effective %d/%d ",
6626*86d7f5d3SJohn Marino pw->pw_gid, getuid(), getgid(), geteuid(), getegid());
6627*86d7f5d3SJohn Marino #endif /* HAVE_SYSLOG_H */
6628*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
6629*86d7f5d3SJohn Marino }
6630*86d7f5d3SJohn Marino }
6631*86d7f5d3SJohn Marino
6632*86d7f5d3SJohn Marino if (setuid (pw->pw_uid) < 0)
6633*86d7f5d3SJohn Marino {
6634*86d7f5d3SJohn Marino /* Note that this means that if run as a non-root user,
6635*86d7f5d3SJohn Marino CVSROOT/passwd must contain the user we are running as
6636*86d7f5d3SJohn Marino (e.g. "joe:FsEfVcu:cvs" if run as "cvs" user). This seems
6637*86d7f5d3SJohn Marino cleaner than ignoring the error like CVS 1.10 and older but
6638*86d7f5d3SJohn Marino it does mean that some people might need to update their
6639*86d7f5d3SJohn Marino CVSROOT/passwd file. */
6640*86d7f5d3SJohn Marino printf ("error 0 setuid failed: %s\n", strerror (errno));
6641*86d7f5d3SJohn Marino #ifdef HAVE_SYSLOG_H
6642*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR,
6643*86d7f5d3SJohn Marino "setuid to %d failed (%m): real %d/%d, effective %d/%d ",
6644*86d7f5d3SJohn Marino pw->pw_uid, getuid(), getgid(), geteuid(), getegid());
6645*86d7f5d3SJohn Marino #endif /* HAVE_SYSLOG_H */
6646*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
6647*86d7f5d3SJohn Marino }
6648*86d7f5d3SJohn Marino
6649*86d7f5d3SJohn Marino /* We don't want our umask to change file modes. The modes should
6650*86d7f5d3SJohn Marino be set by the modes used in the repository, and by the umask of
6651*86d7f5d3SJohn Marino the client. */
6652*86d7f5d3SJohn Marino umask (0);
6653*86d7f5d3SJohn Marino
6654*86d7f5d3SJohn Marino #ifdef AUTH_SERVER_SUPPORT
6655*86d7f5d3SJohn Marino /* Make sure our CVS_Username has been set. */
6656*86d7f5d3SJohn Marino if (CVS_Username == NULL)
6657*86d7f5d3SJohn Marino CVS_Username = xstrdup (username);
6658*86d7f5d3SJohn Marino #endif
6659*86d7f5d3SJohn Marino
6660*86d7f5d3SJohn Marino /* Set LOGNAME, USER and CVS_USER in the environment, in case they
6661*86d7f5d3SJohn Marino are already set to something else. */
6662*86d7f5d3SJohn Marino setenv ("LOGNAME", username, 1);
6663*86d7f5d3SJohn Marino setenv ("USER", username, 1);
6664*86d7f5d3SJohn Marino # ifdef AUTH_SERVER_SUPPORT
6665*86d7f5d3SJohn Marino setenv ("CVS_USER", CVS_Username, 1);
6666*86d7f5d3SJohn Marino # endif
6667*86d7f5d3SJohn Marino }
6668*86d7f5d3SJohn Marino #endif
6669*86d7f5d3SJohn Marino
6670*86d7f5d3SJohn Marino #ifdef AUTH_SERVER_SUPPORT
6671*86d7f5d3SJohn Marino
6672*86d7f5d3SJohn Marino extern char *crypt (const char *, const char *);
6673*86d7f5d3SJohn Marino
6674*86d7f5d3SJohn Marino
6675*86d7f5d3SJohn Marino /*
6676*86d7f5d3SJohn Marino * 0 means no entry found for this user.
6677*86d7f5d3SJohn Marino * 1 means entry found and password matches (or found password is empty)
6678*86d7f5d3SJohn Marino * 2 means entry found, but password does not match.
6679*86d7f5d3SJohn Marino *
6680*86d7f5d3SJohn Marino * If 1, host_user_ptr will be set to point at the system
6681*86d7f5d3SJohn Marino * username (i.e., the "real" identity, which may or may not be the
6682*86d7f5d3SJohn Marino * CVS username) of this user; caller may free this. Global
6683*86d7f5d3SJohn Marino * CVS_Username will point at an allocated copy of cvs username (i.e.,
6684*86d7f5d3SJohn Marino * the username argument below).
6685*86d7f5d3SJohn Marino * kff todo: FIXME: last sentence is not true, it applies to caller.
6686*86d7f5d3SJohn Marino */
6687*86d7f5d3SJohn Marino static int
check_repository_password(char * username,char * password,char * repository,char ** host_user_ptr)6688*86d7f5d3SJohn Marino check_repository_password (char *username, char *password, char *repository, char **host_user_ptr)
6689*86d7f5d3SJohn Marino {
6690*86d7f5d3SJohn Marino int retval = 0;
6691*86d7f5d3SJohn Marino FILE *fp;
6692*86d7f5d3SJohn Marino char *filename;
6693*86d7f5d3SJohn Marino char *linebuf = NULL;
6694*86d7f5d3SJohn Marino size_t linebuf_len;
6695*86d7f5d3SJohn Marino int found_it = 0;
6696*86d7f5d3SJohn Marino int namelen;
6697*86d7f5d3SJohn Marino
6698*86d7f5d3SJohn Marino /* We don't use current_parsed_root->directory because it hasn't been
6699*86d7f5d3SJohn Marino * set yet -- our `repository' argument came from the authentication
6700*86d7f5d3SJohn Marino * protocol, not the regular CVS protocol.
6701*86d7f5d3SJohn Marino */
6702*86d7f5d3SJohn Marino
6703*86d7f5d3SJohn Marino filename = xmalloc (strlen (repository)
6704*86d7f5d3SJohn Marino + 1
6705*86d7f5d3SJohn Marino + strlen (CVSROOTADM)
6706*86d7f5d3SJohn Marino + 1
6707*86d7f5d3SJohn Marino + strlen (CVSROOTADM_PASSWD)
6708*86d7f5d3SJohn Marino + 1);
6709*86d7f5d3SJohn Marino
6710*86d7f5d3SJohn Marino (void) sprintf (filename, "%s/%s/%s", repository,
6711*86d7f5d3SJohn Marino CVSROOTADM, CVSROOTADM_PASSWD);
6712*86d7f5d3SJohn Marino
6713*86d7f5d3SJohn Marino fp = CVS_FOPEN (filename, "r");
6714*86d7f5d3SJohn Marino if (fp == NULL)
6715*86d7f5d3SJohn Marino {
6716*86d7f5d3SJohn Marino if (!existence_error (errno))
6717*86d7f5d3SJohn Marino error (0, errno, "cannot open %s", filename);
6718*86d7f5d3SJohn Marino free (filename);
6719*86d7f5d3SJohn Marino return 0;
6720*86d7f5d3SJohn Marino }
6721*86d7f5d3SJohn Marino
6722*86d7f5d3SJohn Marino /* Look for a relevant line -- one with this user's name. */
6723*86d7f5d3SJohn Marino namelen = strlen (username);
6724*86d7f5d3SJohn Marino while (getline (&linebuf, &linebuf_len, fp) >= 0)
6725*86d7f5d3SJohn Marino {
6726*86d7f5d3SJohn Marino if ((strncmp (linebuf, username, namelen) == 0)
6727*86d7f5d3SJohn Marino && (linebuf[namelen] == ':'))
6728*86d7f5d3SJohn Marino {
6729*86d7f5d3SJohn Marino found_it = 1;
6730*86d7f5d3SJohn Marino break;
6731*86d7f5d3SJohn Marino }
6732*86d7f5d3SJohn Marino }
6733*86d7f5d3SJohn Marino if (ferror (fp))
6734*86d7f5d3SJohn Marino error (0, errno, "cannot read %s", filename);
6735*86d7f5d3SJohn Marino if (fclose (fp) < 0)
6736*86d7f5d3SJohn Marino error (0, errno, "cannot close %s", filename);
6737*86d7f5d3SJohn Marino
6738*86d7f5d3SJohn Marino /* If found_it, then linebuf contains the information we need. */
6739*86d7f5d3SJohn Marino if (found_it)
6740*86d7f5d3SJohn Marino {
6741*86d7f5d3SJohn Marino char *found_password, *host_user_tmp;
6742*86d7f5d3SJohn Marino char *non_cvsuser_portion;
6743*86d7f5d3SJohn Marino
6744*86d7f5d3SJohn Marino /* We need to make sure lines such as
6745*86d7f5d3SJohn Marino *
6746*86d7f5d3SJohn Marino * "username::sysuser\n"
6747*86d7f5d3SJohn Marino * "username:\n"
6748*86d7f5d3SJohn Marino * "username: \n"
6749*86d7f5d3SJohn Marino *
6750*86d7f5d3SJohn Marino * all result in a found_password of NULL, but we also need to
6751*86d7f5d3SJohn Marino * make sure that
6752*86d7f5d3SJohn Marino *
6753*86d7f5d3SJohn Marino * "username: :sysuser\n"
6754*86d7f5d3SJohn Marino * "username: <whatever>:sysuser\n"
6755*86d7f5d3SJohn Marino *
6756*86d7f5d3SJohn Marino * continues to result in an impossible password. That way,
6757*86d7f5d3SJohn Marino * an admin would be on safe ground by going in and tacking a
6758*86d7f5d3SJohn Marino * space onto the front of a password to disable the account
6759*86d7f5d3SJohn Marino * (a technique some people use to close accounts
6760*86d7f5d3SJohn Marino * temporarily).
6761*86d7f5d3SJohn Marino */
6762*86d7f5d3SJohn Marino
6763*86d7f5d3SJohn Marino /* Make `non_cvsuser_portion' contain everything after the CVS
6764*86d7f5d3SJohn Marino username, but null out any final newline. */
6765*86d7f5d3SJohn Marino non_cvsuser_portion = linebuf + namelen;
6766*86d7f5d3SJohn Marino strtok (non_cvsuser_portion, "\n");
6767*86d7f5d3SJohn Marino
6768*86d7f5d3SJohn Marino /* If there's a colon now, we just want to inch past it. */
6769*86d7f5d3SJohn Marino if (strchr (non_cvsuser_portion, ':') == non_cvsuser_portion)
6770*86d7f5d3SJohn Marino non_cvsuser_portion++;
6771*86d7f5d3SJohn Marino
6772*86d7f5d3SJohn Marino /* Okay, after this conditional chain, found_password and
6773*86d7f5d3SJohn Marino host_user_tmp will have useful values: */
6774*86d7f5d3SJohn Marino
6775*86d7f5d3SJohn Marino if ((non_cvsuser_portion == NULL)
6776*86d7f5d3SJohn Marino || (strlen (non_cvsuser_portion) == 0)
6777*86d7f5d3SJohn Marino || ((strspn (non_cvsuser_portion, " \t"))
6778*86d7f5d3SJohn Marino == strlen (non_cvsuser_portion)))
6779*86d7f5d3SJohn Marino {
6780*86d7f5d3SJohn Marino found_password = NULL;
6781*86d7f5d3SJohn Marino host_user_tmp = NULL;
6782*86d7f5d3SJohn Marino }
6783*86d7f5d3SJohn Marino else if (strncmp (non_cvsuser_portion, ":", 1) == 0)
6784*86d7f5d3SJohn Marino {
6785*86d7f5d3SJohn Marino found_password = NULL;
6786*86d7f5d3SJohn Marino host_user_tmp = non_cvsuser_portion + 1;
6787*86d7f5d3SJohn Marino if (strlen (host_user_tmp) == 0)
6788*86d7f5d3SJohn Marino host_user_tmp = NULL;
6789*86d7f5d3SJohn Marino }
6790*86d7f5d3SJohn Marino else
6791*86d7f5d3SJohn Marino {
6792*86d7f5d3SJohn Marino found_password = strtok (non_cvsuser_portion, ":");
6793*86d7f5d3SJohn Marino host_user_tmp = strtok (NULL, ":");
6794*86d7f5d3SJohn Marino }
6795*86d7f5d3SJohn Marino
6796*86d7f5d3SJohn Marino /* Of course, maybe there was no system user portion... */
6797*86d7f5d3SJohn Marino if (host_user_tmp == NULL)
6798*86d7f5d3SJohn Marino host_user_tmp = username;
6799*86d7f5d3SJohn Marino
6800*86d7f5d3SJohn Marino /* Verify blank passwords directly, otherwise use crypt(). */
6801*86d7f5d3SJohn Marino if ((found_password == NULL)
6802*86d7f5d3SJohn Marino || ((strcmp (found_password, crypt (password, found_password))
6803*86d7f5d3SJohn Marino == 0)))
6804*86d7f5d3SJohn Marino {
6805*86d7f5d3SJohn Marino /* Give host_user_ptr permanent storage. */
6806*86d7f5d3SJohn Marino *host_user_ptr = xstrdup (host_user_tmp);
6807*86d7f5d3SJohn Marino retval = 1;
6808*86d7f5d3SJohn Marino }
6809*86d7f5d3SJohn Marino else
6810*86d7f5d3SJohn Marino {
6811*86d7f5d3SJohn Marino #ifdef LOG_AUTHPRIV
6812*86d7f5d3SJohn Marino syslog (LOG_AUTHPRIV | LOG_NOTICE,
6813*86d7f5d3SJohn Marino "password mismatch for %s in %s: %s vs. %s", username,
6814*86d7f5d3SJohn Marino repository, crypt(password, found_password), found_password);
6815*86d7f5d3SJohn Marino #endif
6816*86d7f5d3SJohn Marino *host_user_ptr = NULL;
6817*86d7f5d3SJohn Marino retval = 2;
6818*86d7f5d3SJohn Marino }
6819*86d7f5d3SJohn Marino }
6820*86d7f5d3SJohn Marino else /* Didn't find this user, so deny access. */
6821*86d7f5d3SJohn Marino {
6822*86d7f5d3SJohn Marino *host_user_ptr = NULL;
6823*86d7f5d3SJohn Marino retval = 0;
6824*86d7f5d3SJohn Marino }
6825*86d7f5d3SJohn Marino
6826*86d7f5d3SJohn Marino free (filename);
6827*86d7f5d3SJohn Marino if (linebuf)
6828*86d7f5d3SJohn Marino free (linebuf);
6829*86d7f5d3SJohn Marino
6830*86d7f5d3SJohn Marino return retval;
6831*86d7f5d3SJohn Marino }
6832*86d7f5d3SJohn Marino
6833*86d7f5d3SJohn Marino #ifdef HAVE_PAM
6834*86d7f5d3SJohn Marino
6835*86d7f5d3SJohn Marino static int
cvs_pam_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)6836*86d7f5d3SJohn Marino cvs_pam_conv (int num_msg, const struct pam_message **msg,
6837*86d7f5d3SJohn Marino struct pam_response **resp, void *appdata_ptr)
6838*86d7f5d3SJohn Marino {
6839*86d7f5d3SJohn Marino int i;
6840*86d7f5d3SJohn Marino struct pam_response *response;
6841*86d7f5d3SJohn Marino
6842*86d7f5d3SJohn Marino assert (msg && resp);
6843*86d7f5d3SJohn Marino
6844*86d7f5d3SJohn Marino response = xnmalloc (num_msg, sizeof (struct pam_response));
6845*86d7f5d3SJohn Marino memset (response, 0, num_msg * sizeof (struct pam_response));
6846*86d7f5d3SJohn Marino
6847*86d7f5d3SJohn Marino for (i = 0; i < num_msg; i++)
6848*86d7f5d3SJohn Marino {
6849*86d7f5d3SJohn Marino switch (msg[i]->msg_style)
6850*86d7f5d3SJohn Marino {
6851*86d7f5d3SJohn Marino /* PAM wants a username */
6852*86d7f5d3SJohn Marino case PAM_PROMPT_ECHO_ON:
6853*86d7f5d3SJohn Marino assert (pam_username != 0);
6854*86d7f5d3SJohn Marino response[i].resp = xstrdup (pam_username);
6855*86d7f5d3SJohn Marino break;
6856*86d7f5d3SJohn Marino /* PAM wants a password */
6857*86d7f5d3SJohn Marino case PAM_PROMPT_ECHO_OFF:
6858*86d7f5d3SJohn Marino assert (pam_password != 0);
6859*86d7f5d3SJohn Marino response[i].resp = xstrdup (pam_password);
6860*86d7f5d3SJohn Marino break;
6861*86d7f5d3SJohn Marino case PAM_ERROR_MSG:
6862*86d7f5d3SJohn Marino case PAM_TEXT_INFO:
6863*86d7f5d3SJohn Marino printf ("E %s\n", msg[i]->msg);
6864*86d7f5d3SJohn Marino break;
6865*86d7f5d3SJohn Marino /* PAM wants something we don't understand - bail out */
6866*86d7f5d3SJohn Marino default:
6867*86d7f5d3SJohn Marino goto cleanup;
6868*86d7f5d3SJohn Marino }
6869*86d7f5d3SJohn Marino }
6870*86d7f5d3SJohn Marino
6871*86d7f5d3SJohn Marino *resp = response;
6872*86d7f5d3SJohn Marino return PAM_SUCCESS;
6873*86d7f5d3SJohn Marino
6874*86d7f5d3SJohn Marino cleanup:
6875*86d7f5d3SJohn Marino for (i = 0; i < num_msg; i++)
6876*86d7f5d3SJohn Marino {
6877*86d7f5d3SJohn Marino if (response[i].resp)
6878*86d7f5d3SJohn Marino {
6879*86d7f5d3SJohn Marino free (response[i].resp);
6880*86d7f5d3SJohn Marino response[i].resp = 0;
6881*86d7f5d3SJohn Marino }
6882*86d7f5d3SJohn Marino }
6883*86d7f5d3SJohn Marino free (response);
6884*86d7f5d3SJohn Marino return PAM_CONV_ERR;
6885*86d7f5d3SJohn Marino }
6886*86d7f5d3SJohn Marino
6887*86d7f5d3SJohn Marino static int
check_pam_password(char ** username,char * password)6888*86d7f5d3SJohn Marino check_pam_password (char **username, char *password)
6889*86d7f5d3SJohn Marino {
6890*86d7f5d3SJohn Marino int retval, err;
6891*86d7f5d3SJohn Marino struct pam_conv conv = { cvs_pam_conv, 0 };
6892*86d7f5d3SJohn Marino char *pam_stage = "start";
6893*86d7f5d3SJohn Marino
6894*86d7f5d3SJohn Marino pam_username = *username;
6895*86d7f5d3SJohn Marino pam_password = password;
6896*86d7f5d3SJohn Marino
6897*86d7f5d3SJohn Marino retval = pam_start (PAM_SERVICE_NAME, *username, &conv, &pamh);
6898*86d7f5d3SJohn Marino
6899*86d7f5d3SJohn Marino /* sets a dummy tty name which pam modules can check for */
6900*86d7f5d3SJohn Marino if (retval == PAM_SUCCESS)
6901*86d7f5d3SJohn Marino {
6902*86d7f5d3SJohn Marino pam_stage = "set dummy tty";
6903*86d7f5d3SJohn Marino retval = pam_set_item (pamh, PAM_TTY, PAM_SERVICE_NAME);
6904*86d7f5d3SJohn Marino }
6905*86d7f5d3SJohn Marino
6906*86d7f5d3SJohn Marino if (retval == PAM_SUCCESS)
6907*86d7f5d3SJohn Marino {
6908*86d7f5d3SJohn Marino pam_stage = "authenticate";
6909*86d7f5d3SJohn Marino retval = pam_authenticate (pamh, 0);
6910*86d7f5d3SJohn Marino }
6911*86d7f5d3SJohn Marino
6912*86d7f5d3SJohn Marino if (retval == PAM_SUCCESS)
6913*86d7f5d3SJohn Marino {
6914*86d7f5d3SJohn Marino pam_stage = "account";
6915*86d7f5d3SJohn Marino retval = pam_acct_mgmt (pamh, 0);
6916*86d7f5d3SJohn Marino }
6917*86d7f5d3SJohn Marino
6918*86d7f5d3SJohn Marino if (retval == PAM_SUCCESS)
6919*86d7f5d3SJohn Marino {
6920*86d7f5d3SJohn Marino pam_stage = "get pam user";
6921*86d7f5d3SJohn Marino retval = pam_get_item (pamh, PAM_USER, (const void **)username);
6922*86d7f5d3SJohn Marino }
6923*86d7f5d3SJohn Marino
6924*86d7f5d3SJohn Marino if (retval != PAM_SUCCESS)
6925*86d7f5d3SJohn Marino printf ("E PAM %s error: %s\n", pam_stage, pam_strerror (pamh, retval));
6926*86d7f5d3SJohn Marino
6927*86d7f5d3SJohn Marino /* clear the pointers to make sure we don't use these references again */
6928*86d7f5d3SJohn Marino pam_username = 0;
6929*86d7f5d3SJohn Marino pam_password = 0;
6930*86d7f5d3SJohn Marino
6931*86d7f5d3SJohn Marino return retval == PAM_SUCCESS; /* indicate success */
6932*86d7f5d3SJohn Marino }
6933*86d7f5d3SJohn Marino #endif
6934*86d7f5d3SJohn Marino
6935*86d7f5d3SJohn Marino static int
check_system_password(char * username,char * password)6936*86d7f5d3SJohn Marino check_system_password (char *username, char *password)
6937*86d7f5d3SJohn Marino {
6938*86d7f5d3SJohn Marino char *found_passwd = NULL;
6939*86d7f5d3SJohn Marino struct passwd *pw;
6940*86d7f5d3SJohn Marino #ifdef HAVE_GETSPNAM
6941*86d7f5d3SJohn Marino {
6942*86d7f5d3SJohn Marino struct spwd *spw;
6943*86d7f5d3SJohn Marino
6944*86d7f5d3SJohn Marino spw = getspnam (username);
6945*86d7f5d3SJohn Marino if (spw != NULL)
6946*86d7f5d3SJohn Marino found_passwd = spw->sp_pwdp;
6947*86d7f5d3SJohn Marino }
6948*86d7f5d3SJohn Marino #endif
6949*86d7f5d3SJohn Marino
6950*86d7f5d3SJohn Marino if (found_passwd == NULL && (pw = getpwnam (username)) != NULL)
6951*86d7f5d3SJohn Marino found_passwd = pw->pw_passwd;
6952*86d7f5d3SJohn Marino
6953*86d7f5d3SJohn Marino if (found_passwd == NULL)
6954*86d7f5d3SJohn Marino {
6955*86d7f5d3SJohn Marino printf ("E Fatal error, aborting.\n\
6956*86d7f5d3SJohn Marino error 0 %s: no such user\n", username);
6957*86d7f5d3SJohn Marino
6958*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
6959*86d7f5d3SJohn Marino }
6960*86d7f5d3SJohn Marino
6961*86d7f5d3SJohn Marino /* Allow for dain bramaged HPUX passwd aging
6962*86d7f5d3SJohn Marino * - Basically, HPUX adds a comma and some data
6963*86d7f5d3SJohn Marino * about whether the passwd has expired or not
6964*86d7f5d3SJohn Marino * on the end of the passwd field.
6965*86d7f5d3SJohn Marino * - This code replaces the ',' with '\0'.
6966*86d7f5d3SJohn Marino *
6967*86d7f5d3SJohn Marino * FIXME - our workaround is brain damaged too. I'm
6968*86d7f5d3SJohn Marino * guessing that HPUX WANTED other systems to think the
6969*86d7f5d3SJohn Marino * password was wrong so logins would fail if the
6970*86d7f5d3SJohn Marino * system didn't handle expired passwds and the passwd
6971*86d7f5d3SJohn Marino * might be expired. I think the way to go here
6972*86d7f5d3SJohn Marino * is with PAM.
6973*86d7f5d3SJohn Marino */
6974*86d7f5d3SJohn Marino strtok (found_passwd, ",");
6975*86d7f5d3SJohn Marino
6976*86d7f5d3SJohn Marino if (*found_passwd)
6977*86d7f5d3SJohn Marino {
6978*86d7f5d3SJohn Marino /* user exists and has a password */
6979*86d7f5d3SJohn Marino if (strcmp (found_passwd, crypt (password, found_passwd)) == 0)
6980*86d7f5d3SJohn Marino return 1;
6981*86d7f5d3SJohn Marino else
6982*86d7f5d3SJohn Marino {
6983*86d7f5d3SJohn Marino #ifdef LOG_AUTHPRIV
6984*86d7f5d3SJohn Marino syslog (LOG_AUTHPRIV | LOG_NOTICE,
6985*86d7f5d3SJohn Marino "password mismatch for %s: %s vs. %s", username,
6986*86d7f5d3SJohn Marino crypt(password, found_passwd), found_passwd);
6987*86d7f5d3SJohn Marino #endif
6988*86d7f5d3SJohn Marino return 0;
6989*86d7f5d3SJohn Marino }
6990*86d7f5d3SJohn Marino }
6991*86d7f5d3SJohn Marino
6992*86d7f5d3SJohn Marino #ifdef LOG_AUTHPRIV
6993*86d7f5d3SJohn Marino syslog (LOG_AUTHPRIV | LOG_NOTICE,
6994*86d7f5d3SJohn Marino "user %s authenticated because of blank system password",
6995*86d7f5d3SJohn Marino username);
6996*86d7f5d3SJohn Marino #endif
6997*86d7f5d3SJohn Marino return 1;
6998*86d7f5d3SJohn Marino }
6999*86d7f5d3SJohn Marino
7000*86d7f5d3SJohn Marino
7001*86d7f5d3SJohn Marino
7002*86d7f5d3SJohn Marino /* Return a hosting username if password matches, else NULL. */
7003*86d7f5d3SJohn Marino static char *
check_password(char * username,char * password,char * repository)7004*86d7f5d3SJohn Marino check_password (char *username, char *password, char *repository)
7005*86d7f5d3SJohn Marino {
7006*86d7f5d3SJohn Marino int rc;
7007*86d7f5d3SJohn Marino char *host_user = NULL;
7008*86d7f5d3SJohn Marino
7009*86d7f5d3SJohn Marino /* First we see if this user has a password in the CVS-specific
7010*86d7f5d3SJohn Marino password file. If so, that's enough to authenticate with. If
7011*86d7f5d3SJohn Marino not, we'll check /etc/passwd or maybe whatever is configured via PAM. */
7012*86d7f5d3SJohn Marino
7013*86d7f5d3SJohn Marino rc = check_repository_password (username, password, repository,
7014*86d7f5d3SJohn Marino &host_user);
7015*86d7f5d3SJohn Marino
7016*86d7f5d3SJohn Marino if (rc == 2)
7017*86d7f5d3SJohn Marino return NULL;
7018*86d7f5d3SJohn Marino
7019*86d7f5d3SJohn Marino if (rc == 1)
7020*86d7f5d3SJohn Marino /* host_user already set by reference, so just return. */
7021*86d7f5d3SJohn Marino goto handle_return;
7022*86d7f5d3SJohn Marino
7023*86d7f5d3SJohn Marino assert (rc == 0);
7024*86d7f5d3SJohn Marino
7025*86d7f5d3SJohn Marino if (!config->system_auth)
7026*86d7f5d3SJohn Marino {
7027*86d7f5d3SJohn Marino /* Note that the message _does_ distinguish between the case in
7028*86d7f5d3SJohn Marino which we check for a system password and the case in which
7029*86d7f5d3SJohn Marino we do not. It is a real pain to track down why it isn't
7030*86d7f5d3SJohn Marino letting you in if it won't say why, and I am not convinced
7031*86d7f5d3SJohn Marino that the potential information disclosure to an attacker
7032*86d7f5d3SJohn Marino outweighs this. */
7033*86d7f5d3SJohn Marino printf ("error 0 no such user %s in CVSROOT/passwd\n", username);
7034*86d7f5d3SJohn Marino
7035*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
7036*86d7f5d3SJohn Marino }
7037*86d7f5d3SJohn Marino
7038*86d7f5d3SJohn Marino /* No cvs password found, so try /etc/passwd. */
7039*86d7f5d3SJohn Marino #ifdef HAVE_PAM
7040*86d7f5d3SJohn Marino if (check_pam_password (&username, password))
7041*86d7f5d3SJohn Marino #else /* !HAVE_PAM */
7042*86d7f5d3SJohn Marino if (check_system_password (username, password))
7043*86d7f5d3SJohn Marino #endif /* HAVE_PAM */
7044*86d7f5d3SJohn Marino host_user = xstrdup (username);
7045*86d7f5d3SJohn Marino else
7046*86d7f5d3SJohn Marino host_user = NULL;
7047*86d7f5d3SJohn Marino
7048*86d7f5d3SJohn Marino #ifdef LOG_AUTHPRIV
7049*86d7f5d3SJohn Marino if (!host_user)
7050*86d7f5d3SJohn Marino syslog (LOG_AUTHPRIV | LOG_NOTICE,
7051*86d7f5d3SJohn Marino "login refused for %s: user has no password", username);
7052*86d7f5d3SJohn Marino #endif
7053*86d7f5d3SJohn Marino
7054*86d7f5d3SJohn Marino handle_return:
7055*86d7f5d3SJohn Marino if (host_user)
7056*86d7f5d3SJohn Marino {
7057*86d7f5d3SJohn Marino /* Set CVS_Username here, in allocated space.
7058*86d7f5d3SJohn Marino It might or might not be the same as host_user. */
7059*86d7f5d3SJohn Marino CVS_Username = xmalloc (strlen (username) + 1);
7060*86d7f5d3SJohn Marino strcpy (CVS_Username, username);
7061*86d7f5d3SJohn Marino }
7062*86d7f5d3SJohn Marino
7063*86d7f5d3SJohn Marino return host_user;
7064*86d7f5d3SJohn Marino }
7065*86d7f5d3SJohn Marino
7066*86d7f5d3SJohn Marino #endif /* AUTH_SERVER_SUPPORT */
7067*86d7f5d3SJohn Marino
7068*86d7f5d3SJohn Marino #if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
7069*86d7f5d3SJohn Marino
7070*86d7f5d3SJohn Marino static void
pserver_read_line(char ** tmp,size_t * tmp_len)7071*86d7f5d3SJohn Marino pserver_read_line (char **tmp, size_t *tmp_len)
7072*86d7f5d3SJohn Marino {
7073*86d7f5d3SJohn Marino int status;
7074*86d7f5d3SJohn Marino
7075*86d7f5d3SJohn Marino /* Make sure the protocol starts off on the right foot... */
7076*86d7f5d3SJohn Marino status = buf_read_short_line (buf_from_net, tmp, tmp_len, PATH_MAX);
7077*86d7f5d3SJohn Marino if (status == -1)
7078*86d7f5d3SJohn Marino {
7079*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
7080*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_NOTICE,
7081*86d7f5d3SJohn Marino "unexpected EOF encountered during authentication");
7082*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7083*86d7f5d3SJohn Marino error (1, 0, "unexpected EOF encountered during authentication");
7084*86d7f5d3SJohn Marino }
7085*86d7f5d3SJohn Marino if (status == -2)
7086*86d7f5d3SJohn Marino status = ENOMEM;
7087*86d7f5d3SJohn Marino if (status != 0)
7088*86d7f5d3SJohn Marino {
7089*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
7090*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_NOTICE,
7091*86d7f5d3SJohn Marino "error reading from net while validating pserver");
7092*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7093*86d7f5d3SJohn Marino error (1, status, "error reading from net while validating pserver");
7094*86d7f5d3SJohn Marino }
7095*86d7f5d3SJohn Marino }
7096*86d7f5d3SJohn Marino
7097*86d7f5d3SJohn Marino /* Read username and password from client (i.e., stdin).
7098*86d7f5d3SJohn Marino If correct, then switch to run as that user and send an ACK to the
7099*86d7f5d3SJohn Marino client via stdout, else send NACK and die. */
7100*86d7f5d3SJohn Marino void
pserver_authenticate_connection(void)7101*86d7f5d3SJohn Marino pserver_authenticate_connection (void)
7102*86d7f5d3SJohn Marino {
7103*86d7f5d3SJohn Marino char *tmp;
7104*86d7f5d3SJohn Marino #ifdef AUTH_SERVER_SUPPORT
7105*86d7f5d3SJohn Marino char *repository = NULL;
7106*86d7f5d3SJohn Marino char *username = NULL;
7107*86d7f5d3SJohn Marino char *password = NULL;
7108*86d7f5d3SJohn Marino
7109*86d7f5d3SJohn Marino char *host_user;
7110*86d7f5d3SJohn Marino char *descrambled_password;
7111*86d7f5d3SJohn Marino #endif /* AUTH_SERVER_SUPPORT */
7112*86d7f5d3SJohn Marino int verify_and_exit = 0;
7113*86d7f5d3SJohn Marino
7114*86d7f5d3SJohn Marino /* The Authentication Protocol. Client sends:
7115*86d7f5d3SJohn Marino *
7116*86d7f5d3SJohn Marino * BEGIN AUTH REQUEST\n
7117*86d7f5d3SJohn Marino * <REPOSITORY>\n
7118*86d7f5d3SJohn Marino * <USERNAME>\n
7119*86d7f5d3SJohn Marino * <PASSWORD>\n
7120*86d7f5d3SJohn Marino * END AUTH REQUEST\n
7121*86d7f5d3SJohn Marino *
7122*86d7f5d3SJohn Marino * Server uses above information to authenticate, then sends
7123*86d7f5d3SJohn Marino *
7124*86d7f5d3SJohn Marino * I LOVE YOU\n
7125*86d7f5d3SJohn Marino *
7126*86d7f5d3SJohn Marino * if it grants access, else
7127*86d7f5d3SJohn Marino *
7128*86d7f5d3SJohn Marino * I HATE YOU\n
7129*86d7f5d3SJohn Marino *
7130*86d7f5d3SJohn Marino * if it denies access (and it exits if denying).
7131*86d7f5d3SJohn Marino *
7132*86d7f5d3SJohn Marino * When the client is "cvs login", the user does not desire actual
7133*86d7f5d3SJohn Marino * repository access, but would like to confirm the password with
7134*86d7f5d3SJohn Marino * the server. In this case, the start and stop strings are
7135*86d7f5d3SJohn Marino *
7136*86d7f5d3SJohn Marino * BEGIN VERIFICATION REQUEST\n
7137*86d7f5d3SJohn Marino *
7138*86d7f5d3SJohn Marino * and
7139*86d7f5d3SJohn Marino *
7140*86d7f5d3SJohn Marino * END VERIFICATION REQUEST\n
7141*86d7f5d3SJohn Marino *
7142*86d7f5d3SJohn Marino * On a verification request, the server's responses are the same
7143*86d7f5d3SJohn Marino * (with the obvious semantics), but it exits immediately after
7144*86d7f5d3SJohn Marino * sending the response in both cases.
7145*86d7f5d3SJohn Marino *
7146*86d7f5d3SJohn Marino * Why is the repository sent? Well, note that the actual
7147*86d7f5d3SJohn Marino * client/server protocol can't start up until authentication is
7148*86d7f5d3SJohn Marino * successful. But in order to perform authentication, the server
7149*86d7f5d3SJohn Marino * needs to look up the password in the special CVS passwd file,
7150*86d7f5d3SJohn Marino * before trying /etc/passwd. So the client transmits the
7151*86d7f5d3SJohn Marino * repository as part of the "authentication protocol". The
7152*86d7f5d3SJohn Marino * repository will be redundantly retransmitted later, but that's no
7153*86d7f5d3SJohn Marino * big deal.
7154*86d7f5d3SJohn Marino */
7155*86d7f5d3SJohn Marino
7156*86d7f5d3SJohn Marino /* Initialize buffers. */
7157*86d7f5d3SJohn Marino buf_to_net = fd_buffer_initialize (STDOUT_FILENO, 0, NULL, false,
7158*86d7f5d3SJohn Marino outbuf_memory_error);
7159*86d7f5d3SJohn Marino buf_from_net = fd_buffer_initialize (STDIN_FILENO, 0, NULL, true,
7160*86d7f5d3SJohn Marino outbuf_memory_error);
7161*86d7f5d3SJohn Marino
7162*86d7f5d3SJohn Marino #ifdef SO_KEEPALIVE
7163*86d7f5d3SJohn Marino /* Set SO_KEEPALIVE on the socket, so that we don't hang forever
7164*86d7f5d3SJohn Marino if the client dies while we are waiting for input. */
7165*86d7f5d3SJohn Marino {
7166*86d7f5d3SJohn Marino int on = 1;
7167*86d7f5d3SJohn Marino
7168*86d7f5d3SJohn Marino if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE,
7169*86d7f5d3SJohn Marino &on, sizeof on) < 0)
7170*86d7f5d3SJohn Marino {
7171*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
7172*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR, "error setting KEEPALIVE: %m");
7173*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7174*86d7f5d3SJohn Marino }
7175*86d7f5d3SJohn Marino }
7176*86d7f5d3SJohn Marino #endif
7177*86d7f5d3SJohn Marino
7178*86d7f5d3SJohn Marino /* Make sure the protocol starts off on the right foot... */
7179*86d7f5d3SJohn Marino pserver_read_line (&tmp, NULL);
7180*86d7f5d3SJohn Marino
7181*86d7f5d3SJohn Marino if (strcmp (tmp, "BEGIN VERIFICATION REQUEST") == 0)
7182*86d7f5d3SJohn Marino verify_and_exit = 1;
7183*86d7f5d3SJohn Marino else if (strcmp (tmp, "BEGIN AUTH REQUEST") == 0)
7184*86d7f5d3SJohn Marino ;
7185*86d7f5d3SJohn Marino else if (strcmp (tmp, "BEGIN GSSAPI REQUEST") == 0)
7186*86d7f5d3SJohn Marino {
7187*86d7f5d3SJohn Marino #ifdef HAVE_GSSAPI
7188*86d7f5d3SJohn Marino free (tmp);
7189*86d7f5d3SJohn Marino gserver_authenticate_connection ();
7190*86d7f5d3SJohn Marino return;
7191*86d7f5d3SJohn Marino #else
7192*86d7f5d3SJohn Marino error (1, 0, "GSSAPI authentication not supported by this server");
7193*86d7f5d3SJohn Marino #endif
7194*86d7f5d3SJohn Marino }
7195*86d7f5d3SJohn Marino else
7196*86d7f5d3SJohn Marino error (1, 0, "bad auth protocol start: %s", tmp);
7197*86d7f5d3SJohn Marino
7198*86d7f5d3SJohn Marino #ifndef AUTH_SERVER_SUPPORT
7199*86d7f5d3SJohn Marino
7200*86d7f5d3SJohn Marino error (1, 0, "Password authentication not supported by this server");
7201*86d7f5d3SJohn Marino
7202*86d7f5d3SJohn Marino #else /* AUTH_SERVER_SUPPORT */
7203*86d7f5d3SJohn Marino
7204*86d7f5d3SJohn Marino free (tmp);
7205*86d7f5d3SJohn Marino
7206*86d7f5d3SJohn Marino /* Get the three important pieces of information in order. */
7207*86d7f5d3SJohn Marino /* See above comment about error handling. */
7208*86d7f5d3SJohn Marino pserver_read_line (&repository, NULL);
7209*86d7f5d3SJohn Marino pserver_read_line (&username, NULL);
7210*86d7f5d3SJohn Marino pserver_read_line (&password, NULL);
7211*86d7f5d3SJohn Marino
7212*86d7f5d3SJohn Marino /* ... and make sure the protocol ends on the right foot. */
7213*86d7f5d3SJohn Marino /* See above comment about error handling. */
7214*86d7f5d3SJohn Marino pserver_read_line (&tmp, NULL);
7215*86d7f5d3SJohn Marino if (strcmp (tmp,
7216*86d7f5d3SJohn Marino verify_and_exit ?
7217*86d7f5d3SJohn Marino "END VERIFICATION REQUEST" : "END AUTH REQUEST")
7218*86d7f5d3SJohn Marino != 0)
7219*86d7f5d3SJohn Marino {
7220*86d7f5d3SJohn Marino error (1, 0, "bad auth protocol end: %s", tmp);
7221*86d7f5d3SJohn Marino }
7222*86d7f5d3SJohn Marino free (tmp);
7223*86d7f5d3SJohn Marino
7224*86d7f5d3SJohn Marino if (!root_allow_ok (repository))
7225*86d7f5d3SJohn Marino {
7226*86d7f5d3SJohn Marino error (1, 0, "%s: no such repository", repository);
7227*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
7228*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_NOTICE, "login refused for %s", repository);
7229*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7230*86d7f5d3SJohn Marino goto i_hate_you;
7231*86d7f5d3SJohn Marino }
7232*86d7f5d3SJohn Marino
7233*86d7f5d3SJohn Marino /* OK, now parse the config file, so we can use it to control how
7234*86d7f5d3SJohn Marino to check passwords. If there was an error parsing the config
7235*86d7f5d3SJohn Marino file, parse_config already printed an error. We keep going.
7236*86d7f5d3SJohn Marino Why? Because if we didn't, then there would be no way to check
7237*86d7f5d3SJohn Marino in a new CVSROOT/config file to fix the broken one! */
7238*86d7f5d3SJohn Marino config = get_root_allow_config (repository, gConfigPath);
7239*86d7f5d3SJohn Marino
7240*86d7f5d3SJohn Marino /* We need the real cleartext before we hash it. */
7241*86d7f5d3SJohn Marino descrambled_password = descramble (password);
7242*86d7f5d3SJohn Marino host_user = check_password (username, descrambled_password, repository);
7243*86d7f5d3SJohn Marino if (host_user == NULL)
7244*86d7f5d3SJohn Marino {
7245*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
7246*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_NOTICE, "login failure (for %s)", repository);
7247*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7248*86d7f5d3SJohn Marino memset (descrambled_password, 0, strlen (descrambled_password));
7249*86d7f5d3SJohn Marino free (descrambled_password);
7250*86d7f5d3SJohn Marino i_hate_you:
7251*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "I HATE YOU\n");
7252*86d7f5d3SJohn Marino buf_flush (buf_to_net, true);
7253*86d7f5d3SJohn Marino
7254*86d7f5d3SJohn Marino /* Don't worry about server_cleanup, server_active isn't set
7255*86d7f5d3SJohn Marino yet. */
7256*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
7257*86d7f5d3SJohn Marino }
7258*86d7f5d3SJohn Marino memset (descrambled_password, 0, strlen (descrambled_password));
7259*86d7f5d3SJohn Marino free (descrambled_password);
7260*86d7f5d3SJohn Marino
7261*86d7f5d3SJohn Marino /* Don't go any farther if we're just responding to "cvs login". */
7262*86d7f5d3SJohn Marino if (verify_and_exit)
7263*86d7f5d3SJohn Marino {
7264*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "I LOVE YOU\n");
7265*86d7f5d3SJohn Marino buf_flush (buf_to_net, true);
7266*86d7f5d3SJohn Marino exit (EXIT_SUCCESS);
7267*86d7f5d3SJohn Marino }
7268*86d7f5d3SJohn Marino
7269*86d7f5d3SJohn Marino /* Set Pserver_Repos so that we can check later that the same
7270*86d7f5d3SJohn Marino repository is sent in later client/server protocol. */
7271*86d7f5d3SJohn Marino Pserver_Repos = xmalloc (strlen (repository) + 1);
7272*86d7f5d3SJohn Marino strcpy (Pserver_Repos, repository);
7273*86d7f5d3SJohn Marino
7274*86d7f5d3SJohn Marino /* Switch to run as this user. */
7275*86d7f5d3SJohn Marino switch_to_user (username, host_user);
7276*86d7f5d3SJohn Marino free (host_user);
7277*86d7f5d3SJohn Marino free (repository);
7278*86d7f5d3SJohn Marino free (username);
7279*86d7f5d3SJohn Marino free (password);
7280*86d7f5d3SJohn Marino
7281*86d7f5d3SJohn Marino buf_output0 (buf_to_net, "I LOVE YOU\n");
7282*86d7f5d3SJohn Marino buf_flush (buf_to_net, true);
7283*86d7f5d3SJohn Marino #endif /* AUTH_SERVER_SUPPORT */
7284*86d7f5d3SJohn Marino }
7285*86d7f5d3SJohn Marino
7286*86d7f5d3SJohn Marino #endif /* AUTH_SERVER_SUPPORT || HAVE_GSSAPI */
7287*86d7f5d3SJohn Marino
7288*86d7f5d3SJohn Marino
7289*86d7f5d3SJohn Marino #ifdef HAVE_KERBEROS
7290*86d7f5d3SJohn Marino void
kserver_authenticate_connection(void)7291*86d7f5d3SJohn Marino kserver_authenticate_connection( void )
7292*86d7f5d3SJohn Marino {
7293*86d7f5d3SJohn Marino int status;
7294*86d7f5d3SJohn Marino char instance[INST_SZ];
7295*86d7f5d3SJohn Marino struct sockaddr_in peer;
7296*86d7f5d3SJohn Marino struct sockaddr_in laddr;
7297*86d7f5d3SJohn Marino int len;
7298*86d7f5d3SJohn Marino KTEXT_ST ticket;
7299*86d7f5d3SJohn Marino AUTH_DAT auth;
7300*86d7f5d3SJohn Marino char version[KRB_SENDAUTH_VLEN];
7301*86d7f5d3SJohn Marino char user[ANAME_SZ];
7302*86d7f5d3SJohn Marino
7303*86d7f5d3SJohn Marino strcpy (instance, "*");
7304*86d7f5d3SJohn Marino len = sizeof peer;
7305*86d7f5d3SJohn Marino if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &len) < 0
7306*86d7f5d3SJohn Marino || getsockname (STDIN_FILENO, (struct sockaddr *) &laddr,
7307*86d7f5d3SJohn Marino &len) < 0)
7308*86d7f5d3SJohn Marino {
7309*86d7f5d3SJohn Marino printf ("E Fatal error, aborting.\n\
7310*86d7f5d3SJohn Marino error %s getpeername or getsockname failed\n", strerror (errno));
7311*86d7f5d3SJohn Marino
7312*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
7313*86d7f5d3SJohn Marino }
7314*86d7f5d3SJohn Marino
7315*86d7f5d3SJohn Marino #ifdef SO_KEEPALIVE
7316*86d7f5d3SJohn Marino /* Set SO_KEEPALIVE on the socket, so that we don't hang forever
7317*86d7f5d3SJohn Marino if the client dies while we are waiting for input. */
7318*86d7f5d3SJohn Marino {
7319*86d7f5d3SJohn Marino int on = 1;
7320*86d7f5d3SJohn Marino
7321*86d7f5d3SJohn Marino if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE,
7322*86d7f5d3SJohn Marino (char *) &on, sizeof on) < 0)
7323*86d7f5d3SJohn Marino {
7324*86d7f5d3SJohn Marino # ifdef HAVE_SYSLOG_H
7325*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR, "error setting KEEPALIVE: %m");
7326*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7327*86d7f5d3SJohn Marino }
7328*86d7f5d3SJohn Marino }
7329*86d7f5d3SJohn Marino #endif
7330*86d7f5d3SJohn Marino
7331*86d7f5d3SJohn Marino status = krb_recvauth (KOPT_DO_MUTUAL, STDIN_FILENO, &ticket, "rcmd",
7332*86d7f5d3SJohn Marino instance, &peer, &laddr, &auth, "", sched,
7333*86d7f5d3SJohn Marino version);
7334*86d7f5d3SJohn Marino if (status != KSUCCESS)
7335*86d7f5d3SJohn Marino {
7336*86d7f5d3SJohn Marino printf ("E Fatal error, aborting.\n\
7337*86d7f5d3SJohn Marino error 0 kerberos: %s\n", krb_get_err_text(status));
7338*86d7f5d3SJohn Marino
7339*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
7340*86d7f5d3SJohn Marino }
7341*86d7f5d3SJohn Marino
7342*86d7f5d3SJohn Marino memcpy (kblock, auth.session, sizeof (C_Block));
7343*86d7f5d3SJohn Marino
7344*86d7f5d3SJohn Marino /* Get the local name. */
7345*86d7f5d3SJohn Marino status = krb_kntoln (&auth, user);
7346*86d7f5d3SJohn Marino if (status != KSUCCESS)
7347*86d7f5d3SJohn Marino {
7348*86d7f5d3SJohn Marino printf ("E Fatal error, aborting.\n"
7349*86d7f5d3SJohn Marino "error 0 kerberos: can't get local name: %s\n",
7350*86d7f5d3SJohn Marino krb_get_err_text(status));
7351*86d7f5d3SJohn Marino
7352*86d7f5d3SJohn Marino exit (EXIT_FAILURE);
7353*86d7f5d3SJohn Marino }
7354*86d7f5d3SJohn Marino
7355*86d7f5d3SJohn Marino /* Switch to run as this user. */
7356*86d7f5d3SJohn Marino switch_to_user ("Kerberos 4", user);
7357*86d7f5d3SJohn Marino }
7358*86d7f5d3SJohn Marino #endif /* HAVE_KERBEROS */
7359*86d7f5d3SJohn Marino
7360*86d7f5d3SJohn Marino
7361*86d7f5d3SJohn Marino
7362*86d7f5d3SJohn Marino # ifdef HAVE_GSSAPI /* && SERVER_SUPPORT */
7363*86d7f5d3SJohn Marino /* Authenticate a GSSAPI connection. This is called from
7364*86d7f5d3SJohn Marino * pserver_authenticate_connection, and it handles success and failure
7365*86d7f5d3SJohn Marino * the same way.
7366*86d7f5d3SJohn Marino *
7367*86d7f5d3SJohn Marino * GLOBALS
7368*86d7f5d3SJohn Marino * server_hostname The name of this host, as set via a call to
7369*86d7f5d3SJohn Marino * xgethostname() in main().
7370*86d7f5d3SJohn Marino */
7371*86d7f5d3SJohn Marino static void
gserver_authenticate_connection(void)7372*86d7f5d3SJohn Marino gserver_authenticate_connection (void)
7373*86d7f5d3SJohn Marino {
7374*86d7f5d3SJohn Marino char *hn;
7375*86d7f5d3SJohn Marino gss_buffer_desc tok_in, tok_out;
7376*86d7f5d3SJohn Marino char buf[1024];
7377*86d7f5d3SJohn Marino char *credbuf;
7378*86d7f5d3SJohn Marino size_t credbuflen;
7379*86d7f5d3SJohn Marino OM_uint32 stat_min, ret;
7380*86d7f5d3SJohn Marino gss_name_t server_name, client_name;
7381*86d7f5d3SJohn Marino gss_cred_id_t server_creds;
7382*86d7f5d3SJohn Marino int nbytes;
7383*86d7f5d3SJohn Marino gss_OID mechid;
7384*86d7f5d3SJohn Marino
7385*86d7f5d3SJohn Marino hn = canon_host (server_hostname);
7386*86d7f5d3SJohn Marino if (!hn)
7387*86d7f5d3SJohn Marino error (1, 0, "can't get canonical hostname for `%s': %s",
7388*86d7f5d3SJohn Marino server_hostname, ch_strerror ());
7389*86d7f5d3SJohn Marino
7390*86d7f5d3SJohn Marino sprintf (buf, "cvs@%s", hn);
7391*86d7f5d3SJohn Marino free (hn);
7392*86d7f5d3SJohn Marino tok_in.value = buf;
7393*86d7f5d3SJohn Marino tok_in.length = strlen (buf);
7394*86d7f5d3SJohn Marino
7395*86d7f5d3SJohn Marino if (gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE,
7396*86d7f5d3SJohn Marino &server_name) != GSS_S_COMPLETE)
7397*86d7f5d3SJohn Marino error (1, 0, "could not import GSSAPI service name %s", buf);
7398*86d7f5d3SJohn Marino
7399*86d7f5d3SJohn Marino /* Acquire the server credential to verify the client's
7400*86d7f5d3SJohn Marino authentication. */
7401*86d7f5d3SJohn Marino if (gss_acquire_cred (&stat_min, server_name, 0, GSS_C_NULL_OID_SET,
7402*86d7f5d3SJohn Marino GSS_C_ACCEPT, &server_creds,
7403*86d7f5d3SJohn Marino NULL, NULL) != GSS_S_COMPLETE)
7404*86d7f5d3SJohn Marino error (1, 0, "could not acquire GSSAPI server credentials");
7405*86d7f5d3SJohn Marino
7406*86d7f5d3SJohn Marino gss_release_name (&stat_min, &server_name);
7407*86d7f5d3SJohn Marino
7408*86d7f5d3SJohn Marino /* The client will send us a two byte length followed by that many
7409*86d7f5d3SJohn Marino bytes. */
7410*86d7f5d3SJohn Marino if (fread (buf, 1, 2, stdin) != 2)
7411*86d7f5d3SJohn Marino error (1, errno, "read of length failed");
7412*86d7f5d3SJohn Marino
7413*86d7f5d3SJohn Marino nbytes = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
7414*86d7f5d3SJohn Marino if (nbytes <= sizeof buf)
7415*86d7f5d3SJohn Marino {
7416*86d7f5d3SJohn Marino credbuf = buf;
7417*86d7f5d3SJohn Marino credbuflen = sizeof buf;
7418*86d7f5d3SJohn Marino }
7419*86d7f5d3SJohn Marino else
7420*86d7f5d3SJohn Marino {
7421*86d7f5d3SJohn Marino credbuflen = nbytes;
7422*86d7f5d3SJohn Marino credbuf = xmalloc (credbuflen);
7423*86d7f5d3SJohn Marino }
7424*86d7f5d3SJohn Marino
7425*86d7f5d3SJohn Marino if (fread (credbuf, 1, nbytes, stdin) != nbytes)
7426*86d7f5d3SJohn Marino error (1, errno, "read of data failed");
7427*86d7f5d3SJohn Marino
7428*86d7f5d3SJohn Marino gcontext = GSS_C_NO_CONTEXT;
7429*86d7f5d3SJohn Marino tok_in.length = nbytes;
7430*86d7f5d3SJohn Marino tok_in.value = credbuf;
7431*86d7f5d3SJohn Marino
7432*86d7f5d3SJohn Marino if (gss_accept_sec_context (&stat_min,
7433*86d7f5d3SJohn Marino &gcontext, /* context_handle */
7434*86d7f5d3SJohn Marino server_creds, /* verifier_cred_handle */
7435*86d7f5d3SJohn Marino &tok_in, /* input_token */
7436*86d7f5d3SJohn Marino NULL, /* channel bindings */
7437*86d7f5d3SJohn Marino &client_name, /* src_name */
7438*86d7f5d3SJohn Marino &mechid, /* mech_type */
7439*86d7f5d3SJohn Marino &tok_out, /* output_token */
7440*86d7f5d3SJohn Marino &ret,
7441*86d7f5d3SJohn Marino NULL, /* ignore time_rec */
7442*86d7f5d3SJohn Marino NULL) /* ignore del_cred_handle */
7443*86d7f5d3SJohn Marino != GSS_S_COMPLETE)
7444*86d7f5d3SJohn Marino {
7445*86d7f5d3SJohn Marino error (1, 0, "could not verify credentials");
7446*86d7f5d3SJohn Marino }
7447*86d7f5d3SJohn Marino
7448*86d7f5d3SJohn Marino /* FIXME: Use Kerberos v5 specific code to authenticate to a user.
7449*86d7f5d3SJohn Marino We could instead use an authentication to access mapping. */
7450*86d7f5d3SJohn Marino {
7451*86d7f5d3SJohn Marino krb5_context kc;
7452*86d7f5d3SJohn Marino krb5_principal p;
7453*86d7f5d3SJohn Marino gss_buffer_desc desc;
7454*86d7f5d3SJohn Marino
7455*86d7f5d3SJohn Marino krb5_init_context (&kc);
7456*86d7f5d3SJohn Marino if (gss_display_name (&stat_min, client_name, &desc,
7457*86d7f5d3SJohn Marino &mechid) != GSS_S_COMPLETE
7458*86d7f5d3SJohn Marino || krb5_parse_name (kc, ((gss_buffer_t) &desc)->value, &p) != 0
7459*86d7f5d3SJohn Marino || krb5_aname_to_localname (kc, p, sizeof buf, buf) != 0
7460*86d7f5d3SJohn Marino || krb5_kuserok (kc, p, buf) != TRUE)
7461*86d7f5d3SJohn Marino {
7462*86d7f5d3SJohn Marino error (1, 0, "access denied");
7463*86d7f5d3SJohn Marino }
7464*86d7f5d3SJohn Marino krb5_free_principal (kc, p);
7465*86d7f5d3SJohn Marino krb5_free_context (kc);
7466*86d7f5d3SJohn Marino }
7467*86d7f5d3SJohn Marino
7468*86d7f5d3SJohn Marino if (tok_out.length != 0)
7469*86d7f5d3SJohn Marino {
7470*86d7f5d3SJohn Marino char cbuf[2];
7471*86d7f5d3SJohn Marino
7472*86d7f5d3SJohn Marino cbuf[0] = (tok_out.length >> 8) & 0xff;
7473*86d7f5d3SJohn Marino cbuf[1] = tok_out.length & 0xff;
7474*86d7f5d3SJohn Marino if (fwrite (cbuf, 1, 2, stdout) != 2
7475*86d7f5d3SJohn Marino || (fwrite (tok_out.value, 1, tok_out.length, stdout)
7476*86d7f5d3SJohn Marino != tok_out.length))
7477*86d7f5d3SJohn Marino error (1, errno, "fwrite failed");
7478*86d7f5d3SJohn Marino }
7479*86d7f5d3SJohn Marino
7480*86d7f5d3SJohn Marino switch_to_user ("GSSAPI", buf);
7481*86d7f5d3SJohn Marino
7482*86d7f5d3SJohn Marino if (credbuf != buf)
7483*86d7f5d3SJohn Marino free (credbuf);
7484*86d7f5d3SJohn Marino
7485*86d7f5d3SJohn Marino printf ("I LOVE YOU\n");
7486*86d7f5d3SJohn Marino fflush (stdout);
7487*86d7f5d3SJohn Marino }
7488*86d7f5d3SJohn Marino
7489*86d7f5d3SJohn Marino # endif /* HAVE_GSSAPI */
7490*86d7f5d3SJohn Marino
7491*86d7f5d3SJohn Marino #endif /* SERVER_SUPPORT */
7492*86d7f5d3SJohn Marino
7493*86d7f5d3SJohn Marino #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
7494*86d7f5d3SJohn Marino
7495*86d7f5d3SJohn Marino /* This global variable is non-zero if the user requests encryption on
7496*86d7f5d3SJohn Marino the command line. */
7497*86d7f5d3SJohn Marino int cvsencrypt;
7498*86d7f5d3SJohn Marino
7499*86d7f5d3SJohn Marino /* This global variable is non-zero if the users requests stream
7500*86d7f5d3SJohn Marino authentication on the command line. */
7501*86d7f5d3SJohn Marino int cvsauthenticate;
7502*86d7f5d3SJohn Marino
7503*86d7f5d3SJohn Marino #ifdef ENCRYPTION
7504*86d7f5d3SJohn Marino
7505*86d7f5d3SJohn Marino #ifdef HAVE_KERBEROS
7506*86d7f5d3SJohn Marino
7507*86d7f5d3SJohn Marino /* An encryption interface using Kerberos. This is built on top of a
7508*86d7f5d3SJohn Marino packetizing buffer. */
7509*86d7f5d3SJohn Marino
7510*86d7f5d3SJohn Marino /* This structure is the closure field of the Kerberos translation
7511*86d7f5d3SJohn Marino routines. */
7512*86d7f5d3SJohn Marino struct krb_encrypt_data
7513*86d7f5d3SJohn Marino {
7514*86d7f5d3SJohn Marino /* The Kerberos key schedule. */
7515*86d7f5d3SJohn Marino Key_schedule sched;
7516*86d7f5d3SJohn Marino /* The Kerberos DES block. */
7517*86d7f5d3SJohn Marino C_Block block;
7518*86d7f5d3SJohn Marino };
7519*86d7f5d3SJohn Marino
7520*86d7f5d3SJohn Marino
7521*86d7f5d3SJohn Marino
7522*86d7f5d3SJohn Marino /* Decrypt Kerberos data. */
7523*86d7f5d3SJohn Marino static int
krb_encrypt_input(void * fnclosure,const char * input,char * output,int size)7524*86d7f5d3SJohn Marino krb_encrypt_input( void *fnclosure, const char *input, char *output, int size )
7525*86d7f5d3SJohn Marino {
7526*86d7f5d3SJohn Marino struct krb_encrypt_data *kd = (struct krb_encrypt_data *) fnclosure;
7527*86d7f5d3SJohn Marino int tcount;
7528*86d7f5d3SJohn Marino
7529*86d7f5d3SJohn Marino des_cbc_encrypt ((C_Block *) input, (C_Block *) output,
7530*86d7f5d3SJohn Marino size, kd->sched, &kd->block, 0);
7531*86d7f5d3SJohn Marino
7532*86d7f5d3SJohn Marino /* SIZE is the size of the buffer, which is set by the encryption
7533*86d7f5d3SJohn Marino routine. The packetizing buffer will arrange for the first two
7534*86d7f5d3SJohn Marino bytes in the decrypted buffer to be the real (unaligned)
7535*86d7f5d3SJohn Marino length. As a safety check, make sure that the length in the
7536*86d7f5d3SJohn Marino buffer corresponds to SIZE. Note that the length in the buffer
7537*86d7f5d3SJohn Marino is just the length of the data. We must add 2 to account for
7538*86d7f5d3SJohn Marino the buffer count itself. */
7539*86d7f5d3SJohn Marino tcount = ((output[0] & 0xff) << 8) + (output[1] & 0xff);
7540*86d7f5d3SJohn Marino if (((tcount + 2 + 7) & ~7) != size)
7541*86d7f5d3SJohn Marino error (1, 0, "Decryption failure");
7542*86d7f5d3SJohn Marino
7543*86d7f5d3SJohn Marino return 0;
7544*86d7f5d3SJohn Marino }
7545*86d7f5d3SJohn Marino
7546*86d7f5d3SJohn Marino
7547*86d7f5d3SJohn Marino
7548*86d7f5d3SJohn Marino /* Encrypt Kerberos data. */
7549*86d7f5d3SJohn Marino static int
krb_encrypt_output(void * fnclosure,const char * input,char * output,int size,int * translated)7550*86d7f5d3SJohn Marino krb_encrypt_output( void *fnclosure, const char *input, char *output,
7551*86d7f5d3SJohn Marino int size, int *translated )
7552*86d7f5d3SJohn Marino {
7553*86d7f5d3SJohn Marino struct krb_encrypt_data *kd = (struct krb_encrypt_data *) fnclosure;
7554*86d7f5d3SJohn Marino int aligned;
7555*86d7f5d3SJohn Marino
7556*86d7f5d3SJohn Marino /* For security against a known plaintext attack, we should
7557*86d7f5d3SJohn Marino initialize any padding bytes to random values. Instead, we
7558*86d7f5d3SJohn Marino just pick up whatever is on the stack, which is at least better
7559*86d7f5d3SJohn Marino than using zero. */
7560*86d7f5d3SJohn Marino
7561*86d7f5d3SJohn Marino /* Align SIZE to an 8 byte boundary. Note that SIZE includes the
7562*86d7f5d3SJohn Marino two byte buffer count at the start of INPUT which was added by
7563*86d7f5d3SJohn Marino the packetizing buffer. */
7564*86d7f5d3SJohn Marino aligned = (size + 7) & ~7;
7565*86d7f5d3SJohn Marino
7566*86d7f5d3SJohn Marino /* We use des_cbc_encrypt rather than krb_mk_priv because the
7567*86d7f5d3SJohn Marino latter sticks a timestamp in the block, and krb_rd_priv expects
7568*86d7f5d3SJohn Marino that timestamp to be within five minutes of the current time.
7569*86d7f5d3SJohn Marino Given the way the CVS server buffers up data, that can easily
7570*86d7f5d3SJohn Marino fail over a long network connection. We trust krb_recvauth to
7571*86d7f5d3SJohn Marino guard against a replay attack. */
7572*86d7f5d3SJohn Marino
7573*86d7f5d3SJohn Marino des_cbc_encrypt ((C_Block *) input, (C_Block *) output, aligned,
7574*86d7f5d3SJohn Marino kd->sched, &kd->block, 1);
7575*86d7f5d3SJohn Marino
7576*86d7f5d3SJohn Marino *translated = aligned;
7577*86d7f5d3SJohn Marino
7578*86d7f5d3SJohn Marino return 0;
7579*86d7f5d3SJohn Marino }
7580*86d7f5d3SJohn Marino
7581*86d7f5d3SJohn Marino
7582*86d7f5d3SJohn Marino
7583*86d7f5d3SJohn Marino /* Create a Kerberos encryption buffer. We use a packetizing buffer
7584*86d7f5d3SJohn Marino with Kerberos encryption translation routines. */
7585*86d7f5d3SJohn Marino struct buffer *
krb_encrypt_buffer_initialize(struct buffer * buf,int input,Key_schedule sched,C_Block block,void * memory (struct buffer *))7586*86d7f5d3SJohn Marino krb_encrypt_buffer_initialize( struct buffer *buf, int input,
7587*86d7f5d3SJohn Marino Key_schedule sched, C_Block block,
7588*86d7f5d3SJohn Marino void *memory( struct buffer * ) )
7589*86d7f5d3SJohn Marino {
7590*86d7f5d3SJohn Marino struct krb_encrypt_data *kd;
7591*86d7f5d3SJohn Marino
7592*86d7f5d3SJohn Marino kd = (struct krb_encrypt_data *) xmalloc (sizeof *kd);
7593*86d7f5d3SJohn Marino memcpy (kd->sched, sched, sizeof (Key_schedule));
7594*86d7f5d3SJohn Marino memcpy (kd->block, block, sizeof (C_Block));
7595*86d7f5d3SJohn Marino
7596*86d7f5d3SJohn Marino return packetizing_buffer_initialize (buf,
7597*86d7f5d3SJohn Marino input ? krb_encrypt_input : NULL,
7598*86d7f5d3SJohn Marino input ? NULL : krb_encrypt_output,
7599*86d7f5d3SJohn Marino kd,
7600*86d7f5d3SJohn Marino memory);
7601*86d7f5d3SJohn Marino }
7602*86d7f5d3SJohn Marino
7603*86d7f5d3SJohn Marino #endif /* HAVE_KERBEROS */
7604*86d7f5d3SJohn Marino #endif /* ENCRYPTION */
7605*86d7f5d3SJohn Marino #endif /* defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) */
7606*86d7f5d3SJohn Marino
7607*86d7f5d3SJohn Marino
7608*86d7f5d3SJohn Marino
7609*86d7f5d3SJohn Marino /* Output LEN bytes at STR. If LEN is zero, then output up to (not including)
7610*86d7f5d3SJohn Marino the first '\0' byte. */
7611*86d7f5d3SJohn Marino void
cvs_output(const char * str,size_t len)7612*86d7f5d3SJohn Marino cvs_output (const char *str, size_t len)
7613*86d7f5d3SJohn Marino {
7614*86d7f5d3SJohn Marino if (len == 0)
7615*86d7f5d3SJohn Marino len = strlen (str);
7616*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
7617*86d7f5d3SJohn Marino if (error_use_protocol)
7618*86d7f5d3SJohn Marino {
7619*86d7f5d3SJohn Marino if (buf_to_net)
7620*86d7f5d3SJohn Marino {
7621*86d7f5d3SJohn Marino buf_output (saved_output, str, len);
7622*86d7f5d3SJohn Marino buf_copy_lines (buf_to_net, saved_output, 'M');
7623*86d7f5d3SJohn Marino }
7624*86d7f5d3SJohn Marino # if HAVE_SYSLOG_H
7625*86d7f5d3SJohn Marino else
7626*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR,
7627*86d7f5d3SJohn Marino "Attempt to write message after close of network buffer. "
7628*86d7f5d3SJohn Marino "Message was: %s",
7629*86d7f5d3SJohn Marino str);
7630*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7631*86d7f5d3SJohn Marino }
7632*86d7f5d3SJohn Marino else if (server_active)
7633*86d7f5d3SJohn Marino {
7634*86d7f5d3SJohn Marino if (protocol)
7635*86d7f5d3SJohn Marino {
7636*86d7f5d3SJohn Marino buf_output (saved_output, str, len);
7637*86d7f5d3SJohn Marino buf_copy_lines (protocol, saved_output, 'M');
7638*86d7f5d3SJohn Marino buf_send_counted (protocol);
7639*86d7f5d3SJohn Marino }
7640*86d7f5d3SJohn Marino # if HAVE_SYSLOG_H
7641*86d7f5d3SJohn Marino else
7642*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR,
7643*86d7f5d3SJohn Marino "Attempt to write message before initialization of "
7644*86d7f5d3SJohn Marino "protocol buffer. Message was: %s",
7645*86d7f5d3SJohn Marino str);
7646*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7647*86d7f5d3SJohn Marino }
7648*86d7f5d3SJohn Marino else
7649*86d7f5d3SJohn Marino #endif
7650*86d7f5d3SJohn Marino {
7651*86d7f5d3SJohn Marino size_t written;
7652*86d7f5d3SJohn Marino size_t to_write = len;
7653*86d7f5d3SJohn Marino const char *p = str;
7654*86d7f5d3SJohn Marino
7655*86d7f5d3SJohn Marino /* Local users that do 'cvs status 2>&1' on a local repository
7656*86d7f5d3SJohn Marino may see the informational messages out-of-order with the
7657*86d7f5d3SJohn Marino status messages unless we use the fflush (stderr) here. */
7658*86d7f5d3SJohn Marino fflush (stderr);
7659*86d7f5d3SJohn Marino
7660*86d7f5d3SJohn Marino while (to_write > 0)
7661*86d7f5d3SJohn Marino {
7662*86d7f5d3SJohn Marino written = fwrite (p, 1, to_write, stdout);
7663*86d7f5d3SJohn Marino if (written == 0)
7664*86d7f5d3SJohn Marino break;
7665*86d7f5d3SJohn Marino p += written;
7666*86d7f5d3SJohn Marino to_write -= written;
7667*86d7f5d3SJohn Marino }
7668*86d7f5d3SJohn Marino }
7669*86d7f5d3SJohn Marino }
7670*86d7f5d3SJohn Marino
7671*86d7f5d3SJohn Marino /* Output LEN bytes at STR in binary mode. If LEN is zero, then
7672*86d7f5d3SJohn Marino output zero bytes. */
7673*86d7f5d3SJohn Marino
7674*86d7f5d3SJohn Marino void
cvs_output_binary(char * str,size_t len)7675*86d7f5d3SJohn Marino cvs_output_binary (char *str, size_t len)
7676*86d7f5d3SJohn Marino {
7677*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
7678*86d7f5d3SJohn Marino if (error_use_protocol || server_active)
7679*86d7f5d3SJohn Marino {
7680*86d7f5d3SJohn Marino struct buffer *buf;
7681*86d7f5d3SJohn Marino char size_text[40];
7682*86d7f5d3SJohn Marino
7683*86d7f5d3SJohn Marino if (error_use_protocol)
7684*86d7f5d3SJohn Marino buf = buf_to_net;
7685*86d7f5d3SJohn Marino else
7686*86d7f5d3SJohn Marino buf = protocol;
7687*86d7f5d3SJohn Marino
7688*86d7f5d3SJohn Marino assert (buf);
7689*86d7f5d3SJohn Marino
7690*86d7f5d3SJohn Marino if (!supported_response ("Mbinary"))
7691*86d7f5d3SJohn Marino {
7692*86d7f5d3SJohn Marino error (0, 0, "\
7693*86d7f5d3SJohn Marino this client does not support writing binary files to stdout");
7694*86d7f5d3SJohn Marino return;
7695*86d7f5d3SJohn Marino }
7696*86d7f5d3SJohn Marino
7697*86d7f5d3SJohn Marino buf_output0 (buf, "Mbinary\012");
7698*86d7f5d3SJohn Marino sprintf (size_text, "%lu\012", (unsigned long) len);
7699*86d7f5d3SJohn Marino buf_output0 (buf, size_text);
7700*86d7f5d3SJohn Marino
7701*86d7f5d3SJohn Marino /* Not sure what would be involved in using buf_append_data here
7702*86d7f5d3SJohn Marino without stepping on the toes of our caller (which is responsible
7703*86d7f5d3SJohn Marino for the memory allocation of STR). */
7704*86d7f5d3SJohn Marino buf_output (buf, str, len);
7705*86d7f5d3SJohn Marino
7706*86d7f5d3SJohn Marino if (!error_use_protocol)
7707*86d7f5d3SJohn Marino buf_send_counted (protocol);
7708*86d7f5d3SJohn Marino }
7709*86d7f5d3SJohn Marino else
7710*86d7f5d3SJohn Marino #endif
7711*86d7f5d3SJohn Marino {
7712*86d7f5d3SJohn Marino size_t written;
7713*86d7f5d3SJohn Marino size_t to_write = len;
7714*86d7f5d3SJohn Marino const char *p = str;
7715*86d7f5d3SJohn Marino #ifdef USE_SETMODE_STDOUT
7716*86d7f5d3SJohn Marino int oldmode;
7717*86d7f5d3SJohn Marino #endif
7718*86d7f5d3SJohn Marino
7719*86d7f5d3SJohn Marino /* Local users that do 'cvs status 2>&1' on a local repository
7720*86d7f5d3SJohn Marino may see the informational messages out-of-order with the
7721*86d7f5d3SJohn Marino status messages unless we use the fflush (stderr) here. */
7722*86d7f5d3SJohn Marino fflush (stderr);
7723*86d7f5d3SJohn Marino
7724*86d7f5d3SJohn Marino #ifdef USE_SETMODE_STDOUT
7725*86d7f5d3SJohn Marino /* It is possible that this should be the same ifdef as
7726*86d7f5d3SJohn Marino USE_SETMODE_BINARY but at least for the moment we keep them
7727*86d7f5d3SJohn Marino separate. Mostly this is just laziness and/or a question
7728*86d7f5d3SJohn Marino of what has been tested where. Also there might be an
7729*86d7f5d3SJohn Marino issue of setmode vs. _setmode. */
7730*86d7f5d3SJohn Marino /* The Windows doc says to call setmode only right after startup.
7731*86d7f5d3SJohn Marino I assume that what they are talking about can also be helped
7732*86d7f5d3SJohn Marino by flushing the stream before changing the mode. */
7733*86d7f5d3SJohn Marino fflush (stdout);
7734*86d7f5d3SJohn Marino oldmode = _setmode (_fileno (stdout), OPEN_BINARY);
7735*86d7f5d3SJohn Marino if (oldmode < 0)
7736*86d7f5d3SJohn Marino error (0, errno, "failed to setmode on stdout");
7737*86d7f5d3SJohn Marino #endif
7738*86d7f5d3SJohn Marino
7739*86d7f5d3SJohn Marino while (to_write > 0)
7740*86d7f5d3SJohn Marino {
7741*86d7f5d3SJohn Marino written = fwrite (p, 1, to_write, stdout);
7742*86d7f5d3SJohn Marino if (written == 0)
7743*86d7f5d3SJohn Marino break;
7744*86d7f5d3SJohn Marino p += written;
7745*86d7f5d3SJohn Marino to_write -= written;
7746*86d7f5d3SJohn Marino }
7747*86d7f5d3SJohn Marino #ifdef USE_SETMODE_STDOUT
7748*86d7f5d3SJohn Marino fflush (stdout);
7749*86d7f5d3SJohn Marino if (_setmode (_fileno (stdout), oldmode) != OPEN_BINARY)
7750*86d7f5d3SJohn Marino error (0, errno, "failed to setmode on stdout");
7751*86d7f5d3SJohn Marino #endif
7752*86d7f5d3SJohn Marino }
7753*86d7f5d3SJohn Marino }
7754*86d7f5d3SJohn Marino
7755*86d7f5d3SJohn Marino
7756*86d7f5d3SJohn Marino
7757*86d7f5d3SJohn Marino /* Like CVS_OUTPUT but output is for stderr not stdout. */
7758*86d7f5d3SJohn Marino void
cvs_outerr(const char * str,size_t len)7759*86d7f5d3SJohn Marino cvs_outerr (const char *str, size_t len)
7760*86d7f5d3SJohn Marino {
7761*86d7f5d3SJohn Marino if (len == 0)
7762*86d7f5d3SJohn Marino len = strlen (str);
7763*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
7764*86d7f5d3SJohn Marino if (error_use_protocol)
7765*86d7f5d3SJohn Marino {
7766*86d7f5d3SJohn Marino if (buf_to_net)
7767*86d7f5d3SJohn Marino {
7768*86d7f5d3SJohn Marino buf_output (saved_outerr, str, len);
7769*86d7f5d3SJohn Marino buf_copy_lines (buf_to_net, saved_outerr, 'E');
7770*86d7f5d3SJohn Marino }
7771*86d7f5d3SJohn Marino # if HAVE_SYSLOG_H
7772*86d7f5d3SJohn Marino else
7773*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR,
7774*86d7f5d3SJohn Marino "Attempt to write error message after close of network "
7775*86d7f5d3SJohn Marino "buffer. Message was: `%s'",
7776*86d7f5d3SJohn Marino str);
7777*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7778*86d7f5d3SJohn Marino }
7779*86d7f5d3SJohn Marino else if (server_active)
7780*86d7f5d3SJohn Marino {
7781*86d7f5d3SJohn Marino if (protocol)
7782*86d7f5d3SJohn Marino {
7783*86d7f5d3SJohn Marino buf_output (saved_outerr, str, len);
7784*86d7f5d3SJohn Marino buf_copy_lines (protocol, saved_outerr, 'E');
7785*86d7f5d3SJohn Marino buf_send_counted (protocol);
7786*86d7f5d3SJohn Marino }
7787*86d7f5d3SJohn Marino # if HAVE_SYSLOG_H
7788*86d7f5d3SJohn Marino else
7789*86d7f5d3SJohn Marino syslog (LOG_DAEMON | LOG_ERR,
7790*86d7f5d3SJohn Marino "Attempt to write error message before initialization of "
7791*86d7f5d3SJohn Marino "protocol buffer. Message was: `%s'",
7792*86d7f5d3SJohn Marino str);
7793*86d7f5d3SJohn Marino # endif /* HAVE_SYSLOG_H */
7794*86d7f5d3SJohn Marino }
7795*86d7f5d3SJohn Marino else
7796*86d7f5d3SJohn Marino #endif
7797*86d7f5d3SJohn Marino {
7798*86d7f5d3SJohn Marino size_t written;
7799*86d7f5d3SJohn Marino size_t to_write = len;
7800*86d7f5d3SJohn Marino const char *p = str;
7801*86d7f5d3SJohn Marino
7802*86d7f5d3SJohn Marino /* Make sure that output appears in order if stdout and stderr
7803*86d7f5d3SJohn Marino point to the same place. For the server case this is taken
7804*86d7f5d3SJohn Marino care of by the fact that saved_outerr always holds less
7805*86d7f5d3SJohn Marino than a line. */
7806*86d7f5d3SJohn Marino fflush (stdout);
7807*86d7f5d3SJohn Marino
7808*86d7f5d3SJohn Marino while (to_write > 0)
7809*86d7f5d3SJohn Marino {
7810*86d7f5d3SJohn Marino written = fwrite (p, 1, to_write, stderr);
7811*86d7f5d3SJohn Marino if (written == 0)
7812*86d7f5d3SJohn Marino break;
7813*86d7f5d3SJohn Marino p += written;
7814*86d7f5d3SJohn Marino to_write -= written;
7815*86d7f5d3SJohn Marino }
7816*86d7f5d3SJohn Marino }
7817*86d7f5d3SJohn Marino }
7818*86d7f5d3SJohn Marino
7819*86d7f5d3SJohn Marino
7820*86d7f5d3SJohn Marino
7821*86d7f5d3SJohn Marino /* Flush stderr. stderr is normally flushed automatically, of course,
7822*86d7f5d3SJohn Marino but this function is used to flush information from the server back
7823*86d7f5d3SJohn Marino to the client. */
7824*86d7f5d3SJohn Marino void
cvs_flusherr(void)7825*86d7f5d3SJohn Marino cvs_flusherr (void)
7826*86d7f5d3SJohn Marino {
7827*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
7828*86d7f5d3SJohn Marino if (error_use_protocol)
7829*86d7f5d3SJohn Marino {
7830*86d7f5d3SJohn Marino /* skip the actual stderr flush in this case since the parent process
7831*86d7f5d3SJohn Marino * on the server should only be writing to stdout anyhow
7832*86d7f5d3SJohn Marino */
7833*86d7f5d3SJohn Marino /* Flush what we can to the network, but don't block. */
7834*86d7f5d3SJohn Marino buf_flush (buf_to_net, 0);
7835*86d7f5d3SJohn Marino }
7836*86d7f5d3SJohn Marino else if (server_active)
7837*86d7f5d3SJohn Marino {
7838*86d7f5d3SJohn Marino /* make sure stderr is flushed before we send the flush count on the
7839*86d7f5d3SJohn Marino * protocol pipe
7840*86d7f5d3SJohn Marino */
7841*86d7f5d3SJohn Marino fflush (stderr);
7842*86d7f5d3SJohn Marino /* Send a special count to tell the parent to flush. */
7843*86d7f5d3SJohn Marino buf_send_special_count (protocol, -2);
7844*86d7f5d3SJohn Marino }
7845*86d7f5d3SJohn Marino else
7846*86d7f5d3SJohn Marino #endif
7847*86d7f5d3SJohn Marino fflush (stderr);
7848*86d7f5d3SJohn Marino }
7849*86d7f5d3SJohn Marino
7850*86d7f5d3SJohn Marino
7851*86d7f5d3SJohn Marino
7852*86d7f5d3SJohn Marino /* Make it possible for the user to see what has been written to
7853*86d7f5d3SJohn Marino stdout (it is up to the implementation to decide exactly how far it
7854*86d7f5d3SJohn Marino should go to ensure this). */
7855*86d7f5d3SJohn Marino void
cvs_flushout(void)7856*86d7f5d3SJohn Marino cvs_flushout (void)
7857*86d7f5d3SJohn Marino {
7858*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
7859*86d7f5d3SJohn Marino if (error_use_protocol)
7860*86d7f5d3SJohn Marino {
7861*86d7f5d3SJohn Marino /* Flush what we can to the network, but don't block. */
7862*86d7f5d3SJohn Marino buf_flush (buf_to_net, 0);
7863*86d7f5d3SJohn Marino }
7864*86d7f5d3SJohn Marino else if (server_active)
7865*86d7f5d3SJohn Marino {
7866*86d7f5d3SJohn Marino /* Just do nothing. This is because the code which
7867*86d7f5d3SJohn Marino cvs_flushout replaces, setting stdout to line buffering in
7868*86d7f5d3SJohn Marino main.c, didn't get called in the server child process. But
7869*86d7f5d3SJohn Marino in the future it is quite plausible that we'll want to make
7870*86d7f5d3SJohn Marino this case work analogously to cvs_flusherr.
7871*86d7f5d3SJohn Marino
7872*86d7f5d3SJohn Marino FIXME - DRP - I tried to implement this and triggered the following
7873*86d7f5d3SJohn Marino error: "Protocol error: uncounted data discarded". I don't need
7874*86d7f5d3SJohn Marino this feature right now, so I'm not going to bother with it yet.
7875*86d7f5d3SJohn Marino */
7876*86d7f5d3SJohn Marino buf_send_special_count (protocol, -1);
7877*86d7f5d3SJohn Marino }
7878*86d7f5d3SJohn Marino else
7879*86d7f5d3SJohn Marino #endif
7880*86d7f5d3SJohn Marino fflush (stdout);
7881*86d7f5d3SJohn Marino }
7882*86d7f5d3SJohn Marino
7883*86d7f5d3SJohn Marino
7884*86d7f5d3SJohn Marino
7885*86d7f5d3SJohn Marino /* Output TEXT, tagging it according to TAG. There are lots more
7886*86d7f5d3SJohn Marino details about what TAG means in cvsclient.texi but for the simple
7887*86d7f5d3SJohn Marino case (e.g. non-client/server), TAG is just "newline" to output a
7888*86d7f5d3SJohn Marino newline (in which case TEXT must be NULL), and any other tag to
7889*86d7f5d3SJohn Marino output normal text.
7890*86d7f5d3SJohn Marino
7891*86d7f5d3SJohn Marino Note that there is no way to output either \0 or \n as part of TEXT. */
7892*86d7f5d3SJohn Marino
7893*86d7f5d3SJohn Marino void
cvs_output_tagged(const char * tag,const char * text)7894*86d7f5d3SJohn Marino cvs_output_tagged (const char *tag, const char *text)
7895*86d7f5d3SJohn Marino {
7896*86d7f5d3SJohn Marino if (text != NULL && strchr (text, '\n') != NULL)
7897*86d7f5d3SJohn Marino /* Uh oh. The protocol has no way to cope with this. For now
7898*86d7f5d3SJohn Marino we dump core, although that really isn't such a nice
7899*86d7f5d3SJohn Marino response given that this probably can be caused by newlines
7900*86d7f5d3SJohn Marino in filenames and other causes other than bugs in CVS. Note
7901*86d7f5d3SJohn Marino that we don't want to turn this into "MT newline" because
7902*86d7f5d3SJohn Marino this case is a newline within a tagged item, not a newline
7903*86d7f5d3SJohn Marino as extraneous sugar for the user. */
7904*86d7f5d3SJohn Marino assert (0);
7905*86d7f5d3SJohn Marino
7906*86d7f5d3SJohn Marino /* Start and end tags don't take any text, per cvsclient.texi. */
7907*86d7f5d3SJohn Marino if (tag[0] == '+' || tag[0] == '-')
7908*86d7f5d3SJohn Marino assert (text == NULL);
7909*86d7f5d3SJohn Marino
7910*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
7911*86d7f5d3SJohn Marino if (server_active && supported_response ("MT"))
7912*86d7f5d3SJohn Marino {
7913*86d7f5d3SJohn Marino struct buffer *buf;
7914*86d7f5d3SJohn Marino
7915*86d7f5d3SJohn Marino if (error_use_protocol)
7916*86d7f5d3SJohn Marino buf = buf_to_net;
7917*86d7f5d3SJohn Marino else
7918*86d7f5d3SJohn Marino buf = protocol;
7919*86d7f5d3SJohn Marino
7920*86d7f5d3SJohn Marino buf_output0 (buf, "MT ");
7921*86d7f5d3SJohn Marino buf_output0 (buf, tag);
7922*86d7f5d3SJohn Marino if (text != NULL)
7923*86d7f5d3SJohn Marino {
7924*86d7f5d3SJohn Marino buf_output (buf, " ", 1);
7925*86d7f5d3SJohn Marino buf_output0 (buf, text);
7926*86d7f5d3SJohn Marino }
7927*86d7f5d3SJohn Marino buf_output (buf, "\n", 1);
7928*86d7f5d3SJohn Marino
7929*86d7f5d3SJohn Marino if (!error_use_protocol)
7930*86d7f5d3SJohn Marino buf_send_counted (protocol);
7931*86d7f5d3SJohn Marino }
7932*86d7f5d3SJohn Marino else
7933*86d7f5d3SJohn Marino #endif /* SERVER_SUPPORT */
7934*86d7f5d3SJohn Marino {
7935*86d7f5d3SJohn Marino /* No MT support or we are using a local repository. */
7936*86d7f5d3SJohn Marino if (strcmp (tag, "newline") == 0)
7937*86d7f5d3SJohn Marino cvs_output ("\n", 1);
7938*86d7f5d3SJohn Marino else if (strcmp (tag, "date") == 0)
7939*86d7f5d3SJohn Marino {
7940*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
7941*86d7f5d3SJohn Marino if (server_active)
7942*86d7f5d3SJohn Marino /* Output UTC when running as a server without MT support in
7943*86d7f5d3SJohn Marino * the client since it is likely to be more meaningful than
7944*86d7f5d3SJohn Marino * localtime.
7945*86d7f5d3SJohn Marino */
7946*86d7f5d3SJohn Marino cvs_output (text, 0);
7947*86d7f5d3SJohn Marino else
7948*86d7f5d3SJohn Marino #endif /* SERVER_SUPPORT */
7949*86d7f5d3SJohn Marino {
7950*86d7f5d3SJohn Marino char *date_in = xstrdup (text);
7951*86d7f5d3SJohn Marino char *date = format_date_alloc (date_in);
7952*86d7f5d3SJohn Marino cvs_output (date, 0);
7953*86d7f5d3SJohn Marino free (date);
7954*86d7f5d3SJohn Marino free (date_in);
7955*86d7f5d3SJohn Marino }
7956*86d7f5d3SJohn Marino }
7957*86d7f5d3SJohn Marino else if (text != NULL)
7958*86d7f5d3SJohn Marino cvs_output (text, 0);
7959*86d7f5d3SJohn Marino }
7960*86d7f5d3SJohn Marino }
7961*86d7f5d3SJohn Marino
7962*86d7f5d3SJohn Marino
7963*86d7f5d3SJohn Marino
7964*86d7f5d3SJohn Marino /*
7965*86d7f5d3SJohn Marino * void cvs_trace(int level, const char *fmt, ...)
7966*86d7f5d3SJohn Marino *
7967*86d7f5d3SJohn Marino * Print tracing information to stderr on request. Levels are implemented
7968*86d7f5d3SJohn Marino * as with CVSNT.
7969*86d7f5d3SJohn Marino */
7970*86d7f5d3SJohn Marino void
cvs_trace(int level,const char * fmt,...)7971*86d7f5d3SJohn Marino cvs_trace (int level, const char *fmt, ...)
7972*86d7f5d3SJohn Marino {
7973*86d7f5d3SJohn Marino if (trace >= level)
7974*86d7f5d3SJohn Marino {
7975*86d7f5d3SJohn Marino va_list va;
7976*86d7f5d3SJohn Marino
7977*86d7f5d3SJohn Marino va_start (va, fmt);
7978*86d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
7979*86d7f5d3SJohn Marino fprintf (stderr,"%c -> ",server_active?(isProxyServer()?'P':'S'):' ');
7980*86d7f5d3SJohn Marino #else /* ! SERVER_SUPPORT */
7981*86d7f5d3SJohn Marino fprintf (stderr," -> ");
7982*86d7f5d3SJohn Marino #endif
7983*86d7f5d3SJohn Marino vfprintf (stderr, fmt, va);
7984*86d7f5d3SJohn Marino fprintf (stderr,"\n");
7985*86d7f5d3SJohn Marino va_end (va);
7986*86d7f5d3SJohn Marino }
7987*86d7f5d3SJohn Marino }
7988