1 /* 2 * Copyright (C) 1994-2005 The Free Software Foundation, Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2, or (at your option) 7 * any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 #include <sys/cdefs.h> 15 __RCSID("$NetBSD: release.c,v 1.3 2016/05/17 14:00:09 christos Exp $"); 16 17 /* 18 * Release: "cancel" a checkout in the history log. 19 * 20 * - Enter a line in the history log indicating the "release". - If asked to, 21 * delete the local working directory. 22 */ 23 24 #include "cvs.h" 25 #include "save-cwd.h" 26 #include "getline.h" 27 #include "yesno.h" 28 29 static const char *const release_usage[] = 30 { 31 "Usage: %s %s [-d] directories...\n", 32 "\t-d\tDelete the given directory.\n", 33 "(Specify the --help global option for a list of other help options)\n", 34 NULL 35 }; 36 37 #ifdef SERVER_SUPPORT 38 static int release_server (int argc, char **argv); 39 40 /* This is the server side of cvs release. */ 41 static int 42 release_server (int argc, char **argv) 43 { 44 int i; 45 46 /* Note that we skip argv[0]. */ 47 for (i = 1; i < argc; ++i) 48 history_write ('F', argv[i], "", argv[i], ""); 49 return 0; 50 } 51 52 #endif /* SERVER_SUPPORT */ 53 54 /* Construct an update command. Be sure to add authentication and 55 * encryption if we are using them currently, else our child process may 56 * not be able to communicate with the server. 57 */ 58 static FILE * 59 setup_update_command (pid_t *child_pid) 60 { 61 int tofd, fromfd; 62 63 run_setup (program_path); 64 65 #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) 66 /* Be sure to add authentication and encryption if we are using them 67 * currently, else our child process may not be able to communicate with 68 * the server. 69 */ 70 if (cvsauthenticate) run_add_arg ("-a"); 71 if (cvsencrypt) run_add_arg ("-x"); 72 #endif 73 74 /* Don't really change anything. */ 75 run_add_arg ("-n"); 76 77 /* Don't need full verbosity. */ 78 run_add_arg ("-q"); 79 80 /* Propogate our CVSROOT. */ 81 run_add_arg ("-d"); 82 run_add_arg (original_parsed_root->original); 83 84 run_add_arg ("update"); 85 *child_pid = run_piped (&tofd, &fromfd); 86 if (*child_pid < 0) 87 error (1, 0, "could not fork server process"); 88 89 close (tofd); 90 91 return fdopen (fromfd, "r"); 92 } 93 94 95 96 static int 97 close_update_command (FILE *fp, pid_t child_pid) 98 { 99 int status; 100 pid_t w; 101 102 do 103 w = waitpid (child_pid, &status, 0); 104 while (w == -1 && errno == EINTR); 105 if (w == -1) 106 error (1, errno, "waiting for process %d", child_pid); 107 108 return status; 109 } 110 111 112 113 /* There are various things to improve about this implementation: 114 115 1. Using run_popen to run "cvs update" could be replaced by a 116 fairly simple start_recursion/classify_file loop--a win for 117 portability, performance, and cleanliness. In particular, there is 118 no particularly good way to find the right "cvs". 119 120 2. The fact that "cvs update" contacts the server slows things down; 121 it undermines the case for using "cvs release" rather than "rm -rf". 122 However, for correctly printing "? foo" and correctly handling 123 CVSROOTADM_IGNORE, we currently need to contact the server. (One 124 idea for how to fix this is to stash a copy of CVSROOTADM_IGNORE in 125 the working directories; see comment at base_* in entries.c for a 126 few thoughts on that). 127 128 3. Would be nice to take processing things on the client side one step 129 further, and making it like edit/unedit in terms of working well if 130 disconnected from the network, and then sending a delayed 131 notification. 132 133 4. Having separate network turnarounds for the "Notify" request 134 which we do as part of unedit, and for the "release" itself, is slow 135 and unnecessary. */ 136 137 int 138 release (int argc, char **argv) 139 { 140 FILE *fp; 141 int i, c; 142 char *line = NULL; 143 size_t line_allocated = 0; 144 char *thisarg; 145 int arg_start_idx; 146 int err = 0; 147 short delete_flag = 0; 148 struct saved_cwd cwd; 149 150 #ifdef SERVER_SUPPORT 151 if (server_active) 152 return release_server (argc, argv); 153 #endif 154 155 /* Everything from here on is client or local. */ 156 if (argc == -1) 157 usage (release_usage); 158 getoptreset (); 159 while ((c = getopt (argc, argv, "+Qdq")) != -1) 160 { 161 switch (c) 162 { 163 case 'Q': 164 case 'q': 165 error (1, 0, 166 "-q or -Q must be specified before \"%s\"", 167 cvs_cmd_name); 168 break; 169 case 'd': 170 delete_flag++; 171 break; 172 case '?': 173 default: 174 usage (release_usage); 175 break; 176 } 177 } 178 argc -= optind; 179 argv += optind; 180 181 /* We're going to run "cvs -n -q update" and check its output; if 182 * the output is sufficiently unalarming, then we release with no 183 * questions asked. Else we prompt, then maybe release. 184 * (Well, actually we ask no matter what. Our notion of "sufficiently 185 * unalarming" doesn't take into account "? foo.c" files, so it is 186 * up to the user to take note of them, at least currently 187 * (ignore-193 in testsuite)). 188 */ 189 190 #ifdef CLIENT_SUPPORT 191 /* Start the server; we'll close it after looping. */ 192 if (current_parsed_root->isremote) 193 { 194 start_server (); 195 ign_setup (); 196 } 197 #endif /* CLIENT_SUPPORT */ 198 199 /* Remember the directory where "cvs release" was invoked because 200 all args are relative to this directory and we chdir around. 201 */ 202 if (save_cwd (&cwd)) 203 error (1, errno, "Failed to save current directory."); 204 205 arg_start_idx = 0; 206 207 for (i = arg_start_idx; i < argc; i++) 208 { 209 thisarg = argv[i]; 210 211 if (isdir (thisarg)) 212 { 213 if (CVS_CHDIR (thisarg) < 0) 214 { 215 if (!really_quiet) 216 error (0, errno, "can't chdir to: %s", thisarg); 217 continue; 218 } 219 if (!isdir (CVSADM)) 220 { 221 if (!really_quiet) 222 error (0, 0, "no repository directory: %s", thisarg); 223 if (restore_cwd (&cwd)) 224 error (1, errno, 225 "Failed to restore current directory, `%s'.", 226 cwd.name); 227 continue; 228 } 229 } 230 else 231 { 232 if (!really_quiet) 233 error (0, 0, "no such directory: %s", thisarg); 234 continue; 235 } 236 237 if (!really_quiet) 238 { 239 int line_length, status; 240 pid_t child_pid; 241 242 /* The "release" command piggybacks on "update", which 243 does the real work of finding out if anything is not 244 up-to-date with the repository. Then "release" prompts 245 the user, telling her how many files have been 246 modified, and asking if she still wants to do the 247 release. */ 248 fp = setup_update_command (&child_pid); 249 if (fp == NULL) 250 { 251 error (0, 0, "cannot run command:"); 252 run_print (stderr); 253 error (1, 0, "Exiting due to fatal error referenced above."); 254 } 255 256 c = 0; 257 258 while ((line_length = getline (&line, &line_allocated, fp)) >= 0) 259 { 260 if (strchr ("MARCZ", *line)) 261 c++; 262 (void) fputs (line, stdout); 263 } 264 if (line_length < 0 && !feof (fp)) 265 error (0, errno, "cannot read from subprocess"); 266 267 /* If the update exited with an error, then we just want to 268 complain and go on to the next arg. Especially, we do 269 not want to delete the local copy, since it's obviously 270 not what the user thinks it is. */ 271 status = close_update_command (fp, child_pid); 272 if (status != 0) 273 { 274 error (0, 0, "unable to release `%s' (%d)", thisarg, status); 275 if (restore_cwd (&cwd)) 276 error (1, errno, 277 "Failed to restore current directory, `%s'.", 278 cwd.name); 279 continue; 280 } 281 282 printf ("You have [%d] altered files in this repository.\n", 283 c); 284 285 if (!noexec) 286 { 287 printf ("Are you sure you want to release %sdirectory `%s': ", 288 delete_flag ? "(and delete) " : "", thisarg); 289 fflush (stderr); 290 fflush (stdout); 291 if (!yesno ()) /* "No" */ 292 { 293 (void) fprintf (stderr, 294 "** `%s' aborted by user choice.\n", 295 cvs_cmd_name); 296 if (restore_cwd (&cwd)) 297 error (1, errno, 298 "Failed to restore current directory, `%s'.", 299 cwd.name); 300 continue; 301 } 302 } 303 else 304 { 305 if (restore_cwd (&cwd)) 306 error (1, errno, 307 "Failed to restore current directory, `%s'.", 308 cwd.name); 309 continue; 310 } 311 } 312 313 /* Note: client.c doesn't like to have other code 314 changing the current directory on it. So a fair amount 315 of effort is needed to make sure it doesn't get confused 316 about the directory and (for example) overwrite 317 CVS/Entries file in the wrong directory. See release-17 318 through release-23. */ 319 320 if (restore_cwd (&cwd)) 321 error (1, errno, "Failed to restore current directory, `%s'.", 322 cwd.name); 323 324 #ifdef CLIENT_SUPPORT 325 if (!current_parsed_root->isremote 326 || (supported_request ("noop") && supported_request ("Notify"))) 327 #endif 328 { 329 int argc = 2; 330 char *argv[3]; 331 argv[0] = "dummy"; 332 argv[1] = thisarg; 333 argv[2] = NULL; 334 err += unedit (argc, argv); 335 if (restore_cwd (&cwd)) 336 error (1, errno, "Failed to restore current directory, `%s'.", 337 cwd.name); 338 } 339 340 #ifdef CLIENT_SUPPORT 341 if (current_parsed_root->isremote) 342 { 343 send_to_server ("Argument ", 0); 344 send_to_server (thisarg, 0); 345 send_to_server ("\012", 1); 346 send_to_server ("release\012", 0); 347 } 348 else 349 #endif /* CLIENT_SUPPORT */ 350 { 351 history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ 352 } 353 354 if (delete_flag) 355 { 356 /* FIXME? Shouldn't this just delete the CVS-controlled 357 files and, perhaps, the files that would normally be 358 ignored and leave everything else? */ 359 360 if (unlink_file_dir (thisarg) < 0) 361 error (0, errno, "deletion of directory %s failed", thisarg); 362 } 363 364 #ifdef CLIENT_SUPPORT 365 if (current_parsed_root->isremote) 366 { 367 /* FIXME: 368 * Is there a good reason why get_server_responses() isn't 369 * responsible for restoring its initial directory itself when 370 * finished? 371 */ 372 err += get_server_responses (); 373 374 if (restore_cwd (&cwd)) 375 error (1, errno, "Failed to restore current directory, `%s'.", 376 cwd.name); 377 } 378 #endif /* CLIENT_SUPPORT */ 379 } 380 381 if (restore_cwd (&cwd)) 382 error (1, errno, "Failed to restore current directory, `%s'.", 383 cwd.name); 384 free_cwd (&cwd); 385 386 #ifdef CLIENT_SUPPORT 387 if (current_parsed_root->isremote) 388 { 389 /* Unfortunately, client.c doesn't offer a way to close 390 the connection without waiting for responses. The extra 391 network turnaround here is quite unnecessary other than 392 that.... */ 393 send_to_server ("noop\012", 0); 394 err += get_responses_and_close (); 395 } 396 #endif /* CLIENT_SUPPORT */ 397 398 if (line != NULL) 399 free (line); 400 return err; 401 } 402