1 /* utftpd_sccs: sccs support functions */ 2 3 /* 4 * Copyright (C) 1999 Uwe Ohse 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 #include "config.h" 22 #include <sys/types.h> 23 #include <sys/ioctl.h> 24 #include <sys/stat.h> 25 #include <signal.h> 26 #include <fcntl.h> 27 28 #include <sys/socket.h> 29 #include <netinet/in.h> 30 #include "no_tftp.h" 31 #include <netdb.h> 32 33 #include <setjmp.h> 34 #include <syslog.h> 35 #include <errno.h> 36 #include <ctype.h> 37 #include <string.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 #include "uogetopt.h" 41 #include "timselsysdep.h" 42 #include "nonblock.h" 43 #include "str2num.h" 44 #include "uostr.h" 45 #include "uoio.h" 46 #include "cdb.h" 47 #include "sys/wait.h" 48 #include "wildmat.h" 49 #include "utftpd.h" 50 #include "str_ulong.h" 51 52 static ssize_t 53 my_read (int fd, char *buf, size_t bytes) 54 { 55 ssize_t got = 0; 56 while (bytes) { 57 ssize_t t = read (fd, buf, bytes); 58 if (t == -1 && errno != EINTR) return -1; 59 if (t == -1) continue; 60 if (t==0) break; 61 buf += t; 62 bytes -= t; 63 got += t; 64 } 65 return got; 66 } 67 68 static int 69 utftpd_sccs_delta(const char *comment, struct utftpd_ctrl *flags) 70 { 71 const char *slash; 72 static uostr_t s=UOSTR_INIT; 73 static uostr_t t=UOSTR_INIT; /* tmp file */ 74 size_t pos; 75 int target_fd; 76 char numbuf[STR_ULONG]; 77 const char *er; 78 pid_t pid; 79 int is_identical=1; 80 81 slash=strrchr(flags->filename,'/'); 82 if (!uostr_dup_mem(&s,flags->filename,slash-flags->filename+1)) goto oom; 83 pos=s.len; 84 /* try to get an sccs file */ 85 if (!uostr_add_cstr(&s,"SCCS/s.")) goto oom; 86 if (!uostr_add_cstr(&s,slash+1)) goto oom; 87 if (!uostr_add_mem(&s,"",1)) goto oom; 88 89 if (!flags->revision) { 90 /* first: get a copy of the last revision, to make sure we don't checkin the same stuff twice */ 91 /* but since we have a checked out copy here we need to use -p to put the output of sccs into a file, uah ... */ 92 /* so we create a temp file now */ 93 pid=getpid(); 94 if (!uostr_dup_mem(&t,flags->filename,slash-flags->filename+1)) goto oom; 95 str_ulong(numbuf,(unsigned long)pid); 96 if (!uostr_add_cstr(&t,"tmp.")) goto oom; 97 if (!uostr_add_cstr(&t,numbuf)) goto oom; 98 if (!uostr_add_cstr(&t,".")) goto oom; 99 do { 100 static int count=0; 101 size_t pos_target=t.len; 102 if (count==1000) { 103 er=strerror(errno); 104 syslog(LOG_ERR,"can't create temporary file: %s",er); 105 do_nak(flags->remotefd,EUNDEF,"can't create temporary file"); 106 _exit(1); 107 } 108 str_ulong(numbuf,++count); 109 if (!uostr_add_mem(&t,"",1)) goto oom; 110 target_fd=open(t.data,O_RDWR|O_CREAT|O_EXCL,0600); 111 if (target_fd==-1) uostr_cut(&t,pos_target); 112 } while (target_fd==-1); 113 unlink(t.data); 114 115 pid=fork(); 116 if (pid==0) { 117 union {const char **a1; char *const *a2;} d; 118 const char *argv[4]; 119 int oc=0; 120 argv[oc++]="get"; 121 argv[oc++]="-p"; 122 argv[oc++]=strdup(s.data+pos); /* SCCS/s.something */ 123 if (!argv[oc-1]) _exit(1); 124 argv[oc]=0; 125 126 uostr_cut(&s,pos); uostr_add_mem(&s,"",1); 127 if (-1==chdir(s.data)) _exit(1); 128 if (-1==dup2(target_fd,1)) _exit(1); 129 if (-1==dup2(nullfd,2)) _exit(1); 130 131 d.a1=argv; 132 execv(opt_sccs_get,d.a2); 133 _exit(1); 134 } else if (pid==-1) { 135 /* we will then not be able to fork for the checkin, too */ 136 er=strerror(errno); 137 syslog(LOG_ERR,"can't fork: %s",er); 138 do_nak(flags->remotefd,EUNDEF,er); 139 _exit(1); 140 } else { 141 pid=waitpid(pid,0,0); 142 } 143 if (opt_sccs_clean || opt_sccs_unget) { /* that might tell us about NFS casualities. */ 144 char *buf1; 145 char *buf2; 146 #define MYBUF 4096 147 /* we got that file */ 148 int fd1; 149 fd1=open(flags->filename,O_RDONLY); 150 if (fd1==-1) { 151 er=strerror(errno); 152 syslog(LOG_ERR,"can't open %s: %s",flags->filename,er); 153 do_nak(flags->remotefd,EUNDEF,er); 154 _exit(1); 155 } 156 buf1=(char *)malloc(MYBUF); if (!buf1) goto oom; 157 buf2=(char *)malloc(MYBUF); if (!buf2) goto oom; 158 159 if (-1==lseek(target_fd,0,SEEK_SET)) { 160 er=strerror(errno); 161 syslog(LOG_ERR,"can't lseek in %s: %s",t.data,er); 162 do_nak(flags->remotefd,EUNDEF,er); 163 _exit(1); 164 } 165 166 while (1) { 167 ssize_t got1; 168 ssize_t got2; 169 got1=my_read(fd1,buf1,MYBUF); 170 if (got1==-1) { 171 er=strerror(errno); 172 syslog(LOG_ERR,"can't read %s: %s",flags->filename,er); 173 do_nak(flags->remotefd,EUNDEF,er); 174 _exit(1); 175 } 176 got2=my_read(target_fd,buf2,MYBUF); 177 if (got2==-1) { 178 er=strerror(errno); 179 syslog(LOG_ERR,"can't read %s: %s",t.data,er); 180 do_nak(flags->remotefd,EUNDEF,er); 181 _exit(1); 182 } 183 if (got1!=got2 || 0!=memcmp(buf1,buf2,got1)) { is_identical=0; break; } 184 if (got1==0) break; /* EOF */ 185 } 186 close(fd1); 187 close(target_fd); 188 if (is_identical) { 189 syslog(LOG_ERR,"received file %s is identical to repository version",flags->filename); 190 } 191 } else { 192 /* no clean, no unget: treat it as different */ 193 is_identical=0; 194 } 195 } else { 196 /* commit to a revision, so we check it in regardless of whether it is identical or not */ 197 is_identical=0; 198 } 199 200 /* second: check it in */ 201 pid=fork(); 202 if (pid==0) { 203 static uostr_t co=UOSTR_INIT; 204 union {const char **a1; char *const *a2;} d; 205 int oc=0; 206 const char *argv[5]; 207 char *s_file; 208 s_file=strdup(s.data+pos); /* SCCS/s.something */ 209 if (!s_file) _exit(1); 210 uostr_xdup_cstr(&co,"-yby utftpd for "); 211 uostr_xadd_cstr(&co,remoteip); 212 uostr_xadd_cstr(&co," "); 213 if (comment) { 214 uostr_xadd_cstr(&co," "); 215 uostr_xadd_cstr(&co,comment); 216 } 217 uostr_xadd_mem(&co,"",1); 218 uostr_cut(&s,pos); uostr_add_mem(&s,"",1); 219 if (-1==chdir(s.data)) _exit(1); 220 if (-1==dup2(nullfd,2)) _exit(1); 221 if (is_identical && opt_sccs_clean) { 222 argv[oc++]="clean"; 223 argv[oc++]="-u"; 224 argv[oc++]=s_file; 225 argv[oc]=0; 226 d.a1=argv; 227 execv(opt_sccs_clean,d.a2); 228 } else if (is_identical && opt_sccs_unget) { 229 argv[oc++]="unget"; 230 argv[oc++]="-s"; 231 argv[oc++]=s_file; 232 argv[oc++]=0; 233 d.a1=argv; 234 execv(opt_sccs_unget,d.a2); 235 } else { 236 argv[oc++]="delta"; 237 argv[oc++]="-s"; 238 if (flags->revision) { 239 static uostr_t rev=UOSTR_INIT; 240 if (!uostr_add_cstr(&rev,"-R")) _exit(1); 241 if (!uostr_add_cstr(&rev,flags->revision)) _exit(1); 242 if (!uostr_add_mem(&rev,"",1)) _exit(1); 243 argv[oc++]=rev.data; 244 } 245 argv[oc++]=co.data; 246 argv[oc++]=s_file; 247 argv[oc]=0; 248 d.a1=argv; 249 execv(opt_sccs_delta,d.a2); 250 } 251 _exit(1); 252 } else if (pid==-1) { 253 er=strerror(errno); 254 syslog(LOG_ERR,"can't fork: %s",er); 255 do_nak(flags->remotefd,EUNDEF,er); 256 _exit(1); 257 } else { 258 int fail=wait_check_and_log(pid); 259 if (fail) { 260 syslog(LOG_ERR,"version control system failed"); 261 do_nak(flags->remotefd,EUNDEF,"version control system failed"); 262 _exit(1); 263 } 264 } 265 return 0; 266 oom: 267 syslog(LOG_ERR,"out of memory"); 268 do_nak(flags->remotefd,EUNDEF,"out of memory"); 269 exit(1); 270 } 271 272 static int 273 utftpd_sccs_get(int mode, struct utftpd_ctrl *flags) 274 { 275 const char *slash; 276 static uostr_t s=UOSTR_INIT; 277 size_t pos; 278 int fds[2]; 279 const char *er; 280 pid_t pid; 281 282 slash=strrchr(flags->filename,'/'); 283 if (!uostr_dup_mem(&s,flags->filename,slash-flags->filename+1)) goto oom; 284 pos=s.len; 285 /* try to get an sccs file */ 286 if (!uostr_add_cstr(&s,"SCCS/s.")) goto oom; 287 if (!uostr_add_cstr(&s,slash+1)) goto oom; 288 if (!uostr_add_mem(&s,"",1)) goto oom; 289 290 if (mode==TSG_READ && -1==pipe(fds)) { 291 er=strerror(errno); 292 syslog(LOG_ERR,"pipe: %s",er); 293 do_nak(flags->remotefd,EUNDEF,er); 294 _exit(1); 295 } 296 297 pid=fork(); 298 if (pid==0) { 299 union {const char **a1; char *const *a2;} d; 300 const char *argv[10]; /* 6 used */ 301 int oc=0; 302 char *s_file=strdup(s.data+pos); /* SCCS/s.something */ 303 if (!s_file) _exit(1); 304 uostr_cut(&s,pos); uostr_add_mem(&s,"",1); 305 if (-1==chdir(s.data)) _exit(1); 306 if (mode==TSG_READ && -1==dup2(fds[1],1)) _exit(1); 307 if (-1==dup2(nullfd,2)) _exit(1); 308 argv[oc++]="get"; 309 /* in case we want to edit something we check out the latest revision. try_sccs_delta will commit 310 * to the right rev. 311 */ 312 if (mode==TSG_WRITE) argv[oc++]="-e"; 313 else if (flags->revision) { 314 static uostr_t rev=UOSTR_INIT; 315 if (!uostr_add_cstr(&rev,"-r")) _exit(1); 316 if (!uostr_add_cstr(&rev,flags->revision)) _exit(1); 317 if (!uostr_add_mem(&rev,"",1)) _exit(1); 318 argv[oc++]=rev.data; 319 } 320 if (mode==TSG_READ) { 321 argv[oc++]="-p"; /* print to stdout */ 322 close(fds[0]); 323 } 324 argv[oc++]=s_file; 325 argv[oc]=0; 326 d.a1=argv; 327 execv(opt_sccs_get,d.a2); 328 _exit(1); /* just in case */ 329 } else if (pid==-1) { 330 er=strerror(errno); 331 if (mode==TSG_READ) { close(fds[0]); close(fds[1]); } 332 syslog(LOG_ERR,"can't fork: %s",er); 333 do_nak(flags->remotefd,EUNDEF,er); 334 _exit(1); 335 } else { 336 if (mode==TSG_READ) { 337 close(fds[1]); 338 flags->pid=pid; 339 flags->filefd=fds[0]; 340 } else { 341 pid=waitpid(pid,0,0); 342 #define ADD_ON 0 343 #ifndef HAVE_FSYNC 344 # ifdef (O_SYNC) 345 # undef ADD_ON 346 # define ADD_ON O_SYNC 347 # endif 348 #endif 349 flags->filefd=open(flags->filename,O_WRONLY|O_TRUNC|ADD_ON,0644); 350 if (flags->filefd==-1) { 351 syslog(LOG_ERR,"version control system failed, cannot open %s: %s",flags->filename,strerror(errno)); 352 do_nak(flags->remotefd,EUNDEF,"version control system failed"); 353 _exit(1); 354 } 355 } 356 } 357 return 0; 358 oom: 359 syslog(LOG_ERR,"out of memory"); 360 do_nak(flags->remotefd,EUNDEF,"out of memory"); 361 exit(1); 362 } 363 364 static int 365 utftpd_sccs_test(const char *filename, struct utftpd_ctrl *flags) 366 { 367 const char *slash; 368 static uostr_t s=UOSTR_INIT; 369 size_t pos; 370 struct stat st; 371 if (!opt_sccs_delta || !opt_sccs_get) return -1; 372 373 slash=strrchr(filename,'/'); 374 if (!uostr_dup_mem(&s,filename,slash-filename+1)) goto oom; 375 pos=s.len; 376 /* try to get an sccs file */ 377 if (!uostr_add_cstr(&s,"SCCS/s.")) goto oom; 378 if (!uostr_add_cstr(&s,slash+1)) goto oom; 379 if (!uostr_add_mem(&s,"",1)) goto oom; 380 if (0==stat(s.data,&st)) 381 return 0; 382 return -1; 383 oom: 384 syslog(LOG_ERR,"out of memory"); 385 do_nak(flags->remotefd,EUNDEF,"out of memory"); 386 exit(1); 387 } 388 389 static int 390 utftpd_sccs_unget(struct utftpd_ctrl *flags) 391 { 392 const char *slash; 393 static uostr_t s=UOSTR_INIT; 394 size_t pos; 395 pid_t pid; 396 397 slash=strrchr(flags->filename,'/'); 398 if (!uostr_dup_mem(&s,flags->filename,slash-flags->filename+1)) goto oom; 399 pos=s.len; 400 if (!uostr_add_cstr(&s,"SCCS/s.")) goto oom; 401 if (!uostr_add_cstr(&s,slash+1)) goto oom; 402 if (!uostr_add_mem(&s,"",1)) goto oom; 403 404 pid=fork(); 405 if (pid==0) { 406 union {const char **a1; char *const *a2;} d; 407 const char *argv[10]; /* 4 used */ 408 int oc=0; 409 char *s_file; 410 s_file=strdup(s.data+pos); /* SCCS/s.something */ 411 if (!s_file) _exit(1); 412 uostr_cut(&s,pos); uostr_add_mem(&s,"",1); 413 if (-1==chdir(s.data)) _exit(1); 414 if (-1==dup2(nullfd,2)) _exit(1); 415 if (opt_sccs_clean) { 416 argv[oc++]="clean"; 417 argv[oc++]="-u"; 418 argv[oc++]=s_file; 419 argv[oc]=0; 420 d.a1=argv; 421 execv(opt_sccs_clean,d.a2); 422 } else if (opt_sccs_unget) { 423 argv[oc++]="unget"; 424 argv[oc++]="-s"; 425 argv[oc++]=s_file; 426 argv[oc++]=0; 427 d.a1=argv; 428 execv(opt_sccs_unget,d.a2); 429 } 430 _exit(1); 431 } 432 else { 433 int fail=wait_check_and_log(pid); 434 if (fail) { 435 syslog(LOG_ERR,"version control system failed to unget"); 436 _exit(1); 437 } 438 } 439 return 0; 440 oom: 441 syslog(LOG_ERR,"out of memory"); 442 exit(1); 443 } 444 445 struct utftpd_vc utftpd_sccs={ 446 utftpd_sccs_test, 447 utftpd_sccs_delta, 448 utftpd_sccs_get, 449 utftpd_sccs_unget 450 }; 451