int main(int argc, char** argv) { const char *p = argv[1] ?: "/tmp"; char *pattern = strjoina(p, "/systemd-test-XXXXXX"); _cleanup_close_ int fd, fd2; _cleanup_free_ char *cmd, *cmd2, *ans, *ans2; log_set_max_level(LOG_DEBUG); log_parse_environment(); fd = open_tmpfile(p, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0); (void) system(cmd); assert_se(readlink_malloc(cmd + 6, &ans) >= 0); log_debug("link1: %s", ans); assert_se(endswith(ans, " (deleted)")); fd2 = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(unlink(pattern) == 0); assert_se(asprintf(&cmd2, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd2) > 0); (void) system(cmd2); assert_se(readlink_malloc(cmd2 + 6, &ans2) >= 0); log_debug("link2: %s", ans2); assert_se(endswith(ans2, " (deleted)")); return 0; }
static int download_queue_request(CURLM *multi, struct repo_t *repo) { struct stat st; _cleanup_free_ char *url = NULL; if (repo->curl == NULL) { /* it's my first time, be gentle */ if (repo->servercount == 0) { fprintf(stderr, "error: no servers configured for repo %s\n", repo->name); return -1; } repo->curl = curl_easy_init(); snprintf(repo->diskfile, sizeof(repo->diskfile), CACHEPATH "/%s.files", repo->name); curl_easy_setopt(repo->curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(repo->curl, CURLOPT_WRITEFUNCTION, write_handler); curl_easy_setopt(repo->curl, CURLOPT_WRITEDATA, repo); curl_easy_setopt(repo->curl, CURLOPT_PRIVATE, repo); curl_easy_setopt(repo->curl, CURLOPT_ERRORBUFFER, repo->errmsg); curl_easy_setopt(repo->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(repo->curl, CURLOPT_USERAGENT, PACKAGE "/v" PACKAGE_VERSION); repo->tmpfile.fd = open_tmpfile(O_RDWR | O_NONBLOCK); if (repo->tmpfile.fd < 0) { fprintf(stderr, "error: failed to create temporary file for download: %s\n", strerror(-repo->tmpfile.fd)); return -1; } } else { curl_multi_remove_handle(multi, repo->curl); lseek(repo->tmpfile.fd, 0, SEEK_SET); ftruncate(repo->tmpfile.fd, 0); repo->server_idx++; } if (repo->server_idx >= repo->servercount) { fprintf(stderr, "error: failed to update repo: %s\n", repo->name); return -1; } url = prepare_url(repo->servers[repo->server_idx], repo->name, repo->arch); if (url == NULL) { fputs("error: failed to allocate URL for download\n", stderr); return -1; } curl_easy_setopt(repo->curl, CURLOPT_URL, url); if (repo->force == 0 && stat(repo->diskfile, &st) == 0) { curl_easy_setopt(repo->curl, CURLOPT_TIMEVALUE, (long)st.st_mtime); curl_easy_setopt(repo->curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); } repo->dl_time_start = now(); curl_multi_add_handle(multi, repo->curl); return 0; }
int main(int argc, char** argv) { const char *p = argv[1] ?: "/tmp"; char *pattern = strappenda(p, "/systemd-test-XXXXXX"); _cleanup_close_ int fd, fd2; _cleanup_free_ char *cmd, *cmd2; fd = open_tmpfile(p, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0); system(cmd); fd2 = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(unlink(pattern) == 0); assert_se(asprintf(&cmd2, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd2) > 0); system(cmd2); return 0; }
/** * main routine for receiver process. * * Receiver process runs on the same host as the generator process. */ int recv_files(int f_in, char *local_name) { int fd1,fd2; STRUCT_STAT st; int iflags, xlen; char *fname, fbuf[MAXPATHLEN]; char xname[MAXPATHLEN]; char fnametmp[MAXPATHLEN]; char *fnamecmp, *partialptr; char fnamecmpbuf[MAXPATHLEN]; uchar fnamecmp_type; struct file_struct *file; struct stats initial_stats; int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i; enum logcode log_code = log_before_transfer ? FLOG : FINFO; int max_phase = protocol_version >= 29 ? 2 : 1; int dflt_perms = (ACCESSPERMS & ~orig_umask); #ifdef SUPPORT_ACLS const char *parent_dirname = ""; #endif int ndx, recv_ok; if (verbose > 2) rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used); if (delay_updates) delayed_bits = bitbag_create(cur_flist->used + 1); while (1) { cleanup_disable(); /* This call also sets cur_flist. */ ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type, xname, &xlen); if (ndx == NDX_DONE) { if (inc_recurse && first_flist) { if (read_batch) gen_wants_ndx(first_flist->used + first_flist->ndx_start); flist_free(first_flist); if (first_flist) continue; } else if (read_batch && first_flist) gen_wants_ndx(first_flist->used); if (++phase > max_phase) break; if (verbose > 2) rprintf(FINFO, "recv_files phase=%d\n", phase); if (phase == 2 && delay_updates) handle_delayed_updates(local_name); send_msg(MSG_DONE, "", 0, 0); continue; } if (ndx - cur_flist->ndx_start >= 0) file = cur_flist->files[ndx - cur_flist->ndx_start]; else file = dir_flist->files[cur_flist->parent_ndx]; fname = local_name ? local_name : f_name(file, fbuf); if (verbose > 2) rprintf(FINFO, "recv_files(%s)\n", fname); #ifdef SUPPORT_XATTRS if (iflags & ITEM_REPORT_XATTR && !dry_run) recv_xattr_request(file, f_in); #endif if (!(iflags & ITEM_TRANSFER)) { maybe_log_item(file, iflags, itemizing, xname); #ifdef SUPPORT_XATTRS if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && !dry_run) set_file_attrs(fname, file, NULL, fname, 0); #endif continue; } if (phase == 2) { rprintf(FERROR, "got transfer request in phase 2 [%s]\n", who_am_i()); exit_cleanup(RERR_PROTOCOL); } if (file->flags & FLAG_FILE_SENT) { if (csum_length == SHORT_SUM_LENGTH) { if (keep_partial && !partial_dir) make_backups = -make_backups; /* prevents double backup */ if (append_mode) sparse_files = -sparse_files; append_mode = -append_mode; csum_length = SUM_LENGTH; redoing = 1; } } else { if (csum_length != SHORT_SUM_LENGTH) { if (keep_partial && !partial_dir) make_backups = -make_backups; if (append_mode) sparse_files = -sparse_files; append_mode = -append_mode; csum_length = SHORT_SUM_LENGTH; redoing = 0; } } if (!am_server && do_progress) set_current_file_index(file, ndx); stats.num_transferred_files++; stats.total_transferred_size += F_LENGTH(file); cleanup_got_literal = 0; if (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) { rprintf(FERROR, "attempt to hack rsync failed.\n"); exit_cleanup(RERR_PROTOCOL); } if (!do_xfers) { /* log the transfer */ log_item(FCLIENT, file, &stats, iflags, NULL); if (read_batch) discard_receive_data(f_in, F_LENGTH(file)); continue; } if (write_batch < 0) { log_item(FCLIENT, file, &stats, iflags, NULL); if (!am_server) discard_receive_data(f_in, F_LENGTH(file)); continue; } if (read_batch) { if (!(redoing ? we_want_redo(ndx) : gen_wants_ndx(ndx))) { rprintf(FINFO, "(Skipping batched update for%s \"%s\")\n", redoing ? " resend of" : "", fname); discard_receive_data(f_in, F_LENGTH(file)); file->flags |= FLAG_FILE_SENT; continue; } } partialptr = partial_dir ? partial_dir_fname(fname) : fname; if (protocol_version >= 29) { switch (fnamecmp_type) { case FNAMECMP_FNAME: fnamecmp = fname; break; case FNAMECMP_PARTIAL_DIR: fnamecmp = partialptr; break; case FNAMECMP_BACKUP: fnamecmp = get_backup_name(fname); break; case FNAMECMP_FUZZY: if (file->dirname) { pathjoin(fnamecmpbuf, MAXPATHLEN, file->dirname, xname); fnamecmp = fnamecmpbuf; } else fnamecmp = xname; break; default: if (fnamecmp_type >= basis_dir_cnt) { rprintf(FERROR, "invalid basis_dir index: %d.\n", fnamecmp_type); exit_cleanup(RERR_PROTOCOL); } pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname); fnamecmp = fnamecmpbuf; break; } if (!fnamecmp || (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0)) { fnamecmp = fname; fnamecmp_type = FNAMECMP_FNAME; } } else { /* Reminder: --inplace && --partial-dir are never * enabled at the same time. */ if (inplace && make_backups > 0) { if (!(fnamecmp = get_backup_name(fname))) fnamecmp = fname; else fnamecmp_type = FNAMECMP_BACKUP; } else if (partial_dir && partialptr) fnamecmp = partialptr; else fnamecmp = fname; } initial_stats = stats; /* open the file */ fd1 = do_open(fnamecmp, O_RDONLY, 0); if (fd1 == -1 && protocol_version < 29) { if (fnamecmp != fname) { fnamecmp = fname; fd1 = do_open(fnamecmp, O_RDONLY, 0); } if (fd1 == -1 && basis_dir[0]) { /* pre-29 allowed only one alternate basis */ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[0], fname); fnamecmp = fnamecmpbuf; fd1 = do_open(fnamecmp, O_RDONLY, 0); } } updating_basis_or_equiv = inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP); if (fd1 == -1) { st.st_mode = 0; st.st_size = 0; } else if (do_fstat(fd1,&st) != 0) { rsyserr(FERROR_XFER, errno, "fstat %s failed", full_fname(fnamecmp)); discard_receive_data(f_in, F_LENGTH(file)); close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } if (fd1 != -1 && S_ISDIR(st.st_mode) && fnamecmp == fname) { /* this special handling for directories * wouldn't be necessary if robust_rename() * and the underlying robust_unlink could cope * with directories */ rprintf(FERROR_XFER, "recv_files: %s is a directory\n", full_fname(fnamecmp)); discard_receive_data(f_in, F_LENGTH(file)); close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } if (fd1 != -1 && !S_ISREG(st.st_mode)) { close(fd1); fd1 = -1; } /* If we're not preserving permissions, change the file-list's * mode based on the local permissions and some heuristics. */ if (!preserve_perms) { int exists = fd1 != -1; #ifdef SUPPORT_ACLS const char *dn = file->dirname ? file->dirname : "."; if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) { dflt_perms = default_perms_for_dir(dn); parent_dirname = dn; } #endif file->mode = dest_mode(file->mode, st.st_mode, dflt_perms, exists); } /* We now check to see if we are writing the file "inplace" */ if (inplace) { fd2 = do_open(fname, O_WRONLY|O_CREAT, 0600); if (fd2 == -1) { rsyserr(FERROR_XFER, errno, "open %s failed", full_fname(fname)); } } else { fd2 = open_tmpfile(fnametmp, fname, file); if (fd2 != -1) cleanup_set(fnametmp, partialptr, file, fd1, fd2); } if (fd2 == -1) { discard_receive_data(f_in, F_LENGTH(file)); if (fd1 != -1) close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } /* log the transfer */ if (log_before_transfer) log_item(FCLIENT, file, &initial_stats, iflags, NULL); else if (!am_server && verbose && do_progress) rprintf(FINFO, "%s\n", fname); /* recv file data */ recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, F_LENGTH(file)); log_item(log_code, file, &initial_stats, iflags, NULL); if (fd1 != -1) close(fd1); if (close(fd2) < 0) { rsyserr(FERROR, errno, "close failed on %s", full_fname(fnametmp)); exit_cleanup(RERR_FILEIO); } if ((recv_ok && (!delay_updates || !partialptr)) || inplace) { if (partialptr == fname) partialptr = NULL; if (!finish_transfer(fname, fnametmp, fnamecmp, partialptr, file, recv_ok, 1)) recv_ok = -1; else if (fnamecmp == partialptr) { do_unlink(partialptr); handle_partial_dir(partialptr, PDIR_DELETE); } } else if (keep_partial && partialptr) { if (!handle_partial_dir(partialptr, PDIR_CREATE)) { rprintf(FERROR, "Unable to create partial-dir for %s -- discarding %s.\n", local_name ? local_name : f_name(file, NULL), recv_ok ? "completed file" : "partial file"); do_unlink(fnametmp); recv_ok = -1; } else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL, file, recv_ok, !partial_dir)) recv_ok = -1; else if (delay_updates && recv_ok) { bitbag_set_bit(delayed_bits, ndx); recv_ok = 2; } else partialptr = NULL; } else do_unlink(fnametmp); cleanup_disable(); if (read_batch) file->flags |= FLAG_FILE_SENT; switch (recv_ok) { case 2: break; case 1: if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file))) send_msg_int(MSG_SUCCESS, ndx); break; case 0: { enum logcode msgtype = redoing ? FERROR_XFER : FWARNING; if (msgtype == FERROR_XFER || verbose) { char *errstr, *redostr, *keptstr; if (!(keep_partial && partialptr) && !inplace) keptstr = "discarded"; else if (partial_dir) keptstr = "put into partial-dir"; else keptstr = "retained"; if (msgtype == FERROR_XFER) { errstr = "ERROR"; redostr = ""; } else { errstr = "WARNING"; redostr = read_batch ? " (may try again)" : " (will try again)"; } rprintf(msgtype, "%s: %s failed verification -- update %s%s.\n", errstr, local_name ? f_name(file, NULL) : fname, keptstr, redostr); } if (!redoing) { if (read_batch) flist_ndx_push(&batch_redo_list, ndx); send_msg_int(MSG_REDO, ndx); file->flags |= FLAG_FILE_SENT; } else if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); break; } case -1: if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); break; } } if (make_backups < 0) make_backups = -make_backups; if (phase == 2 && delay_updates) /* for protocol_version < 29 */ handle_delayed_updates(local_name); if (verbose > 2) rprintf(FINFO,"recv_files finished\n"); return 0; }
int journal_file_verify( JournalFile *f, const char *key, usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, bool show_progress) { int r; Object *o; uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0; uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; sd_id128_t entry_boot_id; bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0; usec_t last_usec = 0; int data_fd = -1, entry_fd = -1, entry_array_fd = -1; unsigned i; bool found_last = false; #ifdef HAVE_GCRYPT uint64_t last_tag = 0; #endif assert(f); if (key) { #ifdef HAVE_GCRYPT r = journal_file_parse_verification_key(f, key); if (r < 0) { log_error("Failed to parse seed."); return r; } #else return -ENOTSUP; #endif } else if (f->seal) return -ENOKEY; data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); if (data_fd < 0) { log_error_errno(errno, "Failed to create data file: %m"); r = -errno; goto fail; } entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); if (entry_fd < 0) { log_error_errno(errno, "Failed to create entry file: %m"); r = -errno; goto fail; } entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); if (entry_array_fd < 0) { log_error_errno(errno, "Failed to create entry array file: %m"); r = -errno; goto fail; } if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) { log_error("Cannot verify file with unknown extensions."); r = -ENOTSUP; goto fail; } for (i = 0; i < sizeof(f->header->reserved); i++) if (f->header->reserved[i] != 0) { error(offsetof(Header, reserved[i]), "Reserved field is non-zero"); r = -EBADMSG; goto fail; } /* First iteration: we go through all objects, verify the * superficial structure, headers, hashes. */ p = le64toh(f->header->header_size); for (;;) { /* Early exit if there are no objects in the file, at all */ if (le64toh(f->header->tail_object_offset) == 0) break; if (show_progress) draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec); r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); if (r < 0) { error(p, "Invalid object"); goto fail; } if (p > le64toh(f->header->tail_object_offset)) { error(offsetof(Header, tail_object_offset), "Invalid tail object pointer"); r = -EBADMSG; goto fail; } n_objects ++; r = journal_file_object_verify(f, p, o); if (r < 0) { error(p, "Invalid object contents: %s", strerror(-r)); goto fail; } if ((o->object.flags & OBJECT_COMPRESSED_XZ) && (o->object.flags & OBJECT_COMPRESSED_LZ4)) { error(p, "Objected with double compression"); r = -EINVAL; goto fail; } if ((o->object.flags & OBJECT_COMPRESSED_XZ) && !JOURNAL_HEADER_COMPRESSED_XZ(f->header)) { error(p, "XZ compressed object in file without XZ compression"); r = -EBADMSG; goto fail; } if ((o->object.flags & OBJECT_COMPRESSED_LZ4) && !JOURNAL_HEADER_COMPRESSED_LZ4(f->header)) { error(p, "LZ4 compressed object in file without LZ4 compression"); r = -EBADMSG; goto fail; } switch (o->object.type) { case OBJECT_DATA: r = write_uint64(data_fd, p); if (r < 0) goto fail; n_data++; break; case OBJECT_FIELD: n_fields++; break; case OBJECT_ENTRY: if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) { error(p, "First entry before first tag"); r = -EBADMSG; goto fail; } r = write_uint64(entry_fd, p); if (r < 0) goto fail; if (le64toh(o->entry.realtime) < last_tag_realtime) { error(p, "Older entry after newer tag"); r = -EBADMSG; goto fail; } if (!entry_seqnum_set && le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) { error(p, "Head entry sequence number incorrect"); r = -EBADMSG; goto fail; } if (entry_seqnum_set && entry_seqnum >= le64toh(o->entry.seqnum)) { error(p, "Entry sequence number out of synchronization"); r = -EBADMSG; goto fail; } entry_seqnum = le64toh(o->entry.seqnum); entry_seqnum_set = true; if (entry_monotonic_set && sd_id128_equal(entry_boot_id, o->entry.boot_id) && entry_monotonic > le64toh(o->entry.monotonic)) { error(p, "Entry timestamp out of synchronization"); r = -EBADMSG; goto fail; } entry_monotonic = le64toh(o->entry.monotonic); entry_boot_id = o->entry.boot_id; entry_monotonic_set = true; if (!entry_realtime_set && le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) { error(p, "Head entry realtime timestamp incorrect"); r = -EBADMSG; goto fail; } entry_realtime = le64toh(o->entry.realtime); entry_realtime_set = true; n_entries ++; break; case OBJECT_DATA_HASH_TABLE: if (n_data_hash_tables > 1) { error(p, "More than one data hash table"); r = -EBADMSG; goto fail; } if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) || le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { error(p, "header fields for data hash table invalid"); r = -EBADMSG; goto fail; } n_data_hash_tables++; break; case OBJECT_FIELD_HASH_TABLE: if (n_field_hash_tables > 1) { error(p, "More than one field hash table"); r = -EBADMSG; goto fail; } if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) || le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { error(p, "Header fields for field hash table invalid"); r = -EBADMSG; goto fail; } n_field_hash_tables++; break; case OBJECT_ENTRY_ARRAY: r = write_uint64(entry_array_fd, p); if (r < 0) goto fail; if (p == le64toh(f->header->entry_array_offset)) { if (found_main_entry_array) { error(p, "More than one main entry array"); r = -EBADMSG; goto fail; } found_main_entry_array = true; } n_entry_arrays++; break; case OBJECT_TAG: if (!JOURNAL_HEADER_SEALED(f->header)) { error(p, "Tag object in file without sealing"); r = -EBADMSG; goto fail; } if (le64toh(o->tag.seqnum) != n_tags + 1) { error(p, "Tag sequence number out of synchronization"); r = -EBADMSG; goto fail; } if (le64toh(o->tag.epoch) < last_epoch) { error(p, "Epoch sequence out of synchronization"); r = -EBADMSG; goto fail; } #ifdef HAVE_GCRYPT if (f->seal) { uint64_t q, rt; debug(p, "Checking tag %"PRIu64"...", le64toh(o->tag.seqnum)); rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec; if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) { error(p, "tag/entry realtime timestamp out of synchronization"); r = -EBADMSG; goto fail; } /* OK, now we know the epoch. So let's now set * it, and calculate the HMAC for everything * since the last tag. */ r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch)); if (r < 0) goto fail; r = journal_file_hmac_start(f); if (r < 0) goto fail; if (last_tag == 0) { r = journal_file_hmac_put_header(f); if (r < 0) goto fail; q = le64toh(f->header->header_size); } else q = last_tag; while (q <= p) { r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o); if (r < 0) goto fail; r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q); if (r < 0) goto fail; q = q + ALIGN64(le64toh(o->object.size)); } /* Position might have changed, let's reposition things */ r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); if (r < 0) goto fail; if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) { error(p, "Tag failed verification"); r = -EBADMSG; goto fail; } f->hmac_running = false; last_tag_realtime = rt; last_sealed_realtime = entry_realtime; } last_tag = p + ALIGN64(le64toh(o->object.size)); #endif last_epoch = le64toh(o->tag.epoch); n_tags ++; break; default: n_weird ++; } if (p == le64toh(f->header->tail_object_offset)) { found_last = true; break; } p = p + ALIGN64(le64toh(o->object.size)); }; if (!found_last && le64toh(f->header->tail_object_offset) != 0) { error(le64toh(f->header->tail_object_offset), "Tail object pointer dead"); r = -EBADMSG; goto fail; } if (n_objects != le64toh(f->header->n_objects)) { error(offsetof(Header, n_objects), "Object number mismatch"); r = -EBADMSG; goto fail; } if (n_entries != le64toh(f->header->n_entries)) { error(offsetof(Header, n_entries), "Entry number mismatch"); r = -EBADMSG; goto fail; } if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && n_data != le64toh(f->header->n_data)) { error(offsetof(Header, n_data), "Data number mismatch"); r = -EBADMSG; goto fail; } if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) && n_fields != le64toh(f->header->n_fields)) { error(offsetof(Header, n_fields), "Field number mismatch"); r = -EBADMSG; goto fail; } if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) && n_tags != le64toh(f->header->n_tags)) { error(offsetof(Header, n_tags), "Tag number mismatch"); r = -EBADMSG; goto fail; } if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) && n_entry_arrays != le64toh(f->header->n_entry_arrays)) { error(offsetof(Header, n_entry_arrays), "Entry array number mismatch"); r = -EBADMSG; goto fail; } if (!found_main_entry_array && le64toh(f->header->entry_array_offset) != 0) { error(0, "Missing entry array"); r = -EBADMSG; goto fail; } if (entry_seqnum_set && entry_seqnum != le64toh(f->header->tail_entry_seqnum)) { error(offsetof(Header, tail_entry_seqnum), "Invalid tail seqnum"); r = -EBADMSG; goto fail; } if (entry_monotonic_set && (!sd_id128_equal(entry_boot_id, f->header->boot_id) || entry_monotonic != le64toh(f->header->tail_entry_monotonic))) { error(0, "Invalid tail monotonic timestamp"); r = -EBADMSG; goto fail; } if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) { error(0, "Invalid tail realtime timestamp"); r = -EBADMSG; goto fail; } /* Second iteration: we follow all objects referenced from the * two entry points: the object hash table and the entry * array. We also check that everything referenced (directly * or indirectly) in the data hash table also exists in the * entry array, and vice versa. Note that we do not care for * unreferenced objects. We only care that everything that is * referenced is consistent. */ r = verify_entry_array(f, data_fd, n_data, entry_fd, n_entries, entry_array_fd, n_entry_arrays, &last_usec, show_progress); if (r < 0) goto fail; r = verify_hash_table(f, data_fd, n_data, entry_fd, n_entries, entry_array_fd, n_entry_arrays, &last_usec, show_progress); if (r < 0) goto fail; if (show_progress) flush_progress(); mmap_cache_close_fd(f->mmap, data_fd); mmap_cache_close_fd(f->mmap, entry_fd); mmap_cache_close_fd(f->mmap, entry_array_fd); safe_close(data_fd); safe_close(entry_fd); safe_close(entry_array_fd); if (first_contained) *first_contained = le64toh(f->header->head_entry_realtime); if (last_validated) *last_validated = last_sealed_realtime; if (last_contained) *last_contained = le64toh(f->header->tail_entry_realtime); return 0; fail: if (show_progress) flush_progress(); log_error("File corruption detected at %s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).", f->path, p, (unsigned long long) f->last_stat.st_size, 100 * p / f->last_stat.st_size); if (data_fd >= 0) { mmap_cache_close_fd(f->mmap, data_fd); safe_close(data_fd); } if (entry_fd >= 0) { mmap_cache_close_fd(f->mmap, entry_fd); safe_close(entry_fd); } if (entry_array_fd >= 0) { mmap_cache_close_fd(f->mmap, entry_array_fd); safe_close(entry_array_fd); } return r; }
static void open_files(void) { create_file_names(); if (input_file == 0) { input_file = fopen(input_file_name, "r"); if (input_file == 0) open_error(input_file_name); } action_file = open_tmpfile("action_file"); text_file = open_tmpfile("text_file"); if (vflag) { verbose_file = fopen(verbose_file_name, "w"); if (verbose_file == 0) open_error(verbose_file_name); } if (gflag) { graph_file = fopen(graph_file_name, "w"); if (graph_file == 0) open_error(graph_file_name); fprintf(graph_file, "digraph %s {\n", file_prefix); fprintf(graph_file, "\tedge [fontsize=10];\n"); fprintf(graph_file, "\tnode [shape=box,fontsize=10];\n"); fprintf(graph_file, "\torientation=landscape;\n"); fprintf(graph_file, "\trankdir=LR;\n"); fprintf(graph_file, "\t/*\n"); fprintf(graph_file, "\tmargin=0.2;\n"); fprintf(graph_file, "\tpage=\"8.27,11.69\"; // for A4 printing\n"); fprintf(graph_file, "\tratio=auto;\n"); fprintf(graph_file, "\t*/\n"); } if (dflag) { defines_file = fopen(defines_file_name, "w"); if (defines_file == 0) open_error(defines_file_name); union_file = open_tmpfile("union_file"); } if (iflag) { externs_file = fopen(externs_file_name, "w"); if (externs_file == 0) open_error(externs_file_name); } output_file = fopen(output_file_name, "w"); if (output_file == 0) open_error(output_file_name); if (rflag) { code_file = fopen(code_file_name, "w"); if (code_file == 0) open_error(code_file_name); } else code_file = output_file; }
/** * main routine for receiver process. * * Receiver process runs on the same host as the generator process. */ int recv_files(int f_in, char *local_name) { int fd1,fd2; STRUCT_STAT st; int iflags, xlen; char *fname, fbuf[MAXPATHLEN]; char xname[MAXPATHLEN]; char fnametmp[MAXPATHLEN]; char *fnamecmp, *partialptr; char fnamecmpbuf[MAXPATHLEN]; uchar fnamecmp_type; struct file_struct *file; struct stats initial_stats; int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i; enum logcode log_code = log_before_transfer ? FLOG : FINFO; int max_phase = protocol_version >= 29 ? 2 : 1; int dflt_perms = (ACCESSPERMS & ~orig_umask); #if 0 /* was SUPPORT_ACLS */ const char *parent_dirname = ""; #endif int ndx, recv_ok; if (verbose > 2) rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used); if (delay_updates) delayed_bits = bitbag_create(cur_flist->used + 1); while (1) { struct sum_struct sum; int numMatchTokens = -1, nextToken = 0; char *nextData = NULL; static char file_sum[MAX_DIGEST_LEN]; cleanup_disable(); /* This call also sets cur_flist. */ ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type, xname, &xlen); if (ndx == NDX_DONE) { if (inc_recurse && first_flist) { if (read_batch) gen_wants_ndx(first_flist->used + first_flist->ndx_start); flist_free(first_flist); if (first_flist) continue; } else if (read_batch && first_flist) gen_wants_ndx(first_flist->used); if (++phase > max_phase) break; if (verbose > 2) rprintf(FINFO, "recv_files phase=%d\n", phase); if (phase == 2 && delay_updates) handle_delayed_updates(local_name); send_msg(MSG_DONE, "", 0, 0); continue; } if (ndx - cur_flist->ndx_start >= 0) file = cur_flist->files[ndx - cur_flist->ndx_start]; else file = dir_flist->files[cur_flist->parent_ndx]; fname = local_name ? local_name : f_name(file, fbuf); if (verbose > 2) rprintf(FINFO, "recv_files(%s)\n", fname); #ifdef SUPPORT_XATTRS if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers) recv_xattr_request(file, f_in); #endif if (!(iflags & ITEM_TRANSFER)) { maybe_log_item(file, iflags, itemizing, xname); #ifdef SUPPORT_XATTRS if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers && !BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE)) set_file_attrs(fname, file, NULL, fname, 0); #endif continue; } if (phase == 2) { rprintf(FERROR, "got transfer request in phase 2 [%s]\n", who_am_i()); exit_cleanup(RERR_PROTOCOL); } if (file->flags & FLAG_FILE_SENT) { if (csum_length == SHORT_SUM_LENGTH) { if (keep_partial && !partial_dir) make_backups = -make_backups; /* prevents double backup */ if (append_mode) sparse_files = -sparse_files; append_mode = -append_mode; csum_length = SUM_LENGTH; redoing = 1; } } else { if (csum_length != SHORT_SUM_LENGTH) { if (keep_partial && !partial_dir) make_backups = -make_backups; if (append_mode) sparse_files = -sparse_files; append_mode = -append_mode; csum_length = SHORT_SUM_LENGTH; redoing = 0; } } if (!am_server && do_progress) set_current_file_index(file, ndx); stats.num_transferred_files++; stats.total_transferred_size += F_LENGTH(file); cleanup_got_literal = 0; if (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) { rprintf(FERROR, "attempt to hack rsync failed.\n"); exit_cleanup(RERR_PROTOCOL); } read_sum_head(f_in, &sum); if (read_batch) { int wanted = redoing ? we_want_redo(ndx) : gen_wants_ndx(ndx); if (!wanted) { rprintf(FINFO, "(Skipping batched update for%s \"%s\")\n", redoing ? " resend of" : "", fname); discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); file->flags |= FLAG_FILE_SENT; continue; } } if (!do_xfers) { /* log the transfer */ log_item(FCLIENT, file, &stats, iflags, NULL); if (read_batch) discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); continue; } if (write_batch < 0) { log_item(FCLIENT, file, &stats, iflags, NULL); if (!am_server) discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); continue; } partialptr = partial_dir ? partial_dir_fname(fname) : fname; if (protocol_version >= 29) { switch (fnamecmp_type) { case FNAMECMP_FNAME: fnamecmp = fname; break; case FNAMECMP_PARTIAL_DIR: fnamecmp = partialptr; break; case FNAMECMP_BACKUP: fnamecmp = get_backup_name(fname); break; case FNAMECMP_FUZZY: if (file->dirname) { pathjoin(fnamecmpbuf, MAXPATHLEN, file->dirname, xname); fnamecmp = fnamecmpbuf; } else fnamecmp = xname; break; default: if (fnamecmp_type >= basis_dir_cnt) { rprintf(FERROR, "invalid basis_dir index: %d.\n", fnamecmp_type); exit_cleanup(RERR_PROTOCOL); } pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[fnamecmp_type], fname); fnamecmp = fnamecmpbuf; break; } if (!fnamecmp || (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0)) { fnamecmp = fname; fnamecmp_type = FNAMECMP_FNAME; } } else { /* Reminder: --inplace && --partial-dir are never * enabled at the same time. */ if (inplace && make_backups > 0) { if (!(fnamecmp = get_backup_name(fname))) fnamecmp = fname; else fnamecmp_type = FNAMECMP_BACKUP; } else if (partial_dir && partialptr) fnamecmp = partialptr; else fnamecmp = fname; } initial_stats = stats; /* * Opening/reading/writing files in BackupPC is quite expensive, given the * compression. Before we open the file and call mkstemp, we peek ahead at * the delta tokens to see if the file is unchanged. If so, we skip the file * open, mkstemp and writing. The file is unchanged if the tokens * are sequential matching block numbers, which are encoded as * negative integers (ie: -1, -2, -3...), with no literal data * tokens (which have positive numbers). * * If the file has changed, at some point a matching block token * will be out of order, or we will encounter a literal data token. * At that point, we stop reading ahead and proceed as normal. * We pass the number of matching block tokens, and the first * unexpected token, to receive_data(), so it can process all * the tokens back (basically playing them back). * * Of course, this only applies if the file exists already, * and protocol_version 30 is required since older protocols * use MD4 for the full-file digest, not MD5. * * First we need to read the sum struct, then start reading the * tokens. */ recv_ok = 0; if ( protocol_version >= 30 ) { numMatchTokens = 0; while ( (nextToken = recv_token(f_in, &nextData)) != 0 ) { if ( nextToken != -numMatchTokens - 1 ) break; numMatchTokens++; } if ( nextToken == 0 ) { OFF_T flength = (OFF_T)numMatchTokens * sum.blength; if ( sum.remainder && numMatchTokens > 0 ) flength -= sum.blength - sum.remainder; /* * Likely exact match - read the final file digest and make * sure the digest and file size match the existing file. * If so, this call creates the temporary file so the * attributes can be checked/set, and then renamed as * before. */ read_buf(f_in, file_sum, MD5_DIGEST_LEN); if ( !bpc_sysCall_checkFileMatch(fnamecmp, fnametmp, file, file_sum, flength) ) { recv_ok = 1; if ( log_before_transfer ) { iflags &= ~ITEM_REPORT_CHANGE; log_item(FCLIENT, file, &initial_stats, iflags, NULL); } } } } if ( !recv_ok ) { /* * proceed as normal, remembering to replay the tokens we read ahead above: * first, numMatchTokens: -1, -2, ..., -numMatchTokens, * then (nextToken,nextData). After that we go back to reading the * remaining tokens from f_in. */ /* open the file */ fd1 = do_open(fnamecmp, O_RDONLY, 0); if ( fd1 < 0 && protocol_version >= 30 && always_checksum ) { /* * For protocol_version >= 30 and if always_checksum is set, we can use the * MD5 whole-file digest to check for a potential match via the pool. * Use that as the basis if the file is there. The generator does * the same in recv_generator(). */ if ( S_ISREG(file->mode) && !bpc_sysCall_poolFileCheck(fnamecmp, file) ) { fd1 = do_open(fnamecmp, O_RDONLY, 0); } } if (fd1 == -1 && protocol_version < 29) { if (fnamecmp != fname) { fnamecmp = fname; fd1 = do_open(fnamecmp, O_RDONLY, 0); } if (fd1 == -1 && basis_dir[0]) { /* pre-29 allowed only one alternate basis */ pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, basis_dir[0], fname); fnamecmp = fnamecmpbuf; fd1 = do_open(fnamecmp, O_RDONLY, 0); } } updating_basis_or_equiv = inplace && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP); if (fd1 == -1) { st.st_mode = 0; st.st_size = 0; } else if (do_fstat(fd1,&st) != 0) { rsyserr(FERROR_XFER, errno, "fstat %s failed", full_fname(fnamecmp)); discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); bpc_close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } if (fd1 != -1 && S_ISDIR(st.st_mode) && fnamecmp == fname) { /* this special handling for directories * wouldn't be necessary if robust_rename() * and the underlying robust_unlink could cope * with directories */ rprintf(FERROR_XFER, "recv_files: %s is a directory\n", full_fname(fnamecmp)); discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); bpc_close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } if (fd1 != -1 && !S_ISREG(st.st_mode)) { bpc_close(fd1); fd1 = -1; } /* If we're not preserving permissions, change the file-list's * mode based on the local permissions and some heuristics. */ if (!preserve_perms) { int exists = fd1 != -1; #if 0 /* was SUPPORT_ACLS */ const char *dn = file->dirname ? file->dirname : "."; if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) { dflt_perms = default_perms_for_dir(dn); parent_dirname = dn; } #endif file->mode = dest_mode(file->mode, st.st_mode, dflt_perms, exists); } /* We now check to see if we are writing the file "inplace" */ if (inplace) { fd2 = do_open(fname, O_WRONLY|O_CREAT, 0600); if (fd2 == -1) { rsyserr(FERROR_XFER, errno, "open %s failed", full_fname(fname)); } } else { fd2 = open_tmpfile(fnametmp, fname, file); if (fd2 != -1) cleanup_set(fnametmp, partialptr, file, fd1, fd2); } if (fd2 == -1) { discard_receive_data(f_in, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); if (fd1 != -1) bpc_close(fd1); if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); continue; } /* log the transfer */ if (log_before_transfer) log_item(FCLIENT, file, &initial_stats, iflags, NULL); else if (!am_server && verbose && do_progress) rprintf(FINFO, "%s\n", fname); /* recv file data */ recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, F_LENGTH(file), &sum, numMatchTokens, nextToken, nextData, file_sum); if (fd1 != -1) bpc_close(fd1); if (bpc_close(fd2) < 0) { rsyserr(FERROR, errno, "close failed on %s", full_fname(fnametmp)); exit_cleanup(RERR_FILEIO); } } log_item(log_code, file, &initial_stats, iflags, NULL); if ((recv_ok && (!delay_updates || !partialptr)) || inplace) { if (partialptr == fname) partialptr = NULL; if (!finish_transfer(fname, fnametmp, fnamecmp, partialptr, file, recv_ok, 1)) recv_ok = -1; else if (fnamecmp == partialptr) { do_unlink(partialptr); handle_partial_dir(partialptr, PDIR_DELETE); } } else if (keep_partial && partialptr) { if (!handle_partial_dir(partialptr, PDIR_CREATE)) { rprintf(FERROR, "Unable to create partial-dir for %s -- discarding %s.\n", local_name ? local_name : f_name(file, NULL), recv_ok ? "completed file" : "partial file"); do_unlink(fnametmp); recv_ok = -1; } else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL, file, recv_ok, !partial_dir)) recv_ok = -1; else if (delay_updates && recv_ok) { bitbag_set_bit(delayed_bits, ndx); recv_ok = 2; } else partialptr = NULL; } else do_unlink(fnametmp); cleanup_disable(); if (read_batch) file->flags |= FLAG_FILE_SENT; switch (recv_ok) { case 2: break; case 1: if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file))) send_msg_int(MSG_SUCCESS, ndx); break; case 0: { enum logcode msgtype = redoing ? FERROR_XFER : FWARNING; if (msgtype == FERROR_XFER || verbose) { char *errstr, *redostr, *keptstr; if (!(keep_partial && partialptr) && !inplace) keptstr = "discarded"; else if (partial_dir) keptstr = "put into partial-dir"; else keptstr = "retained"; if (msgtype == FERROR_XFER) { errstr = "ERROR"; redostr = ""; } else { errstr = "WARNING"; redostr = read_batch ? " (may try again)" : " (will try again)"; } rprintf(msgtype, "%s: %s failed verification -- update %s%s.\n", errstr, local_name ? f_name(file, NULL) : fname, keptstr, redostr); } if (!redoing) { if (read_batch) flist_ndx_push(&batch_redo_list, ndx); send_msg_int(MSG_REDO, ndx); file->flags |= FLAG_FILE_SENT; bpc_sysCall_printfileStatus(fname, "retry"); } else if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); bpc_sysCall_printfileStatus(fname, "fail"); break; } case -1: if (inc_recurse) send_msg_int(MSG_NO_SEND, ndx); break; } } if (make_backups < 0) make_backups = -make_backups; if (phase == 2 && delay_updates) /* for protocol_version < 29 */ handle_delayed_updates(local_name); if (verbose > 2) rprintf(FINFO,"recv_files finished\n"); return 0; }
int child_execute_job (char *argv, struct child *child) { int i; static struct dsc$descriptor_s cmddsc; static struct dsc$descriptor_s pnamedsc; static struct dsc$descriptor_s ifiledsc; static struct dsc$descriptor_s ofiledsc; static struct dsc$descriptor_s efiledsc; int have_redirection = 0; int have_newline = 0; int spflags = CLI$M_NOWAIT; int status; char *cmd = alloca (strlen (argv) + 512), *p, *q; char ifile[256], ofile[256], efile[256]; char *comname = 0; char procname[100]; int in_string; /* Parse IO redirection. */ ifile[0] = 0; ofile[0] = 0; efile[0] = 0; DB (DB_JOBS, ("child_execute_job (%s)\n", argv)); while (isspace ((unsigned char)*argv)) argv++; if (*argv == 0) return 0; sprintf (procname, "GMAKE_%05x", getpid () & 0xfffff); pnamedsc.dsc$w_length = strlen(procname); pnamedsc.dsc$a_pointer = procname; pnamedsc.dsc$b_dtype = DSC$K_DTYPE_T; pnamedsc.dsc$b_class = DSC$K_CLASS_S; in_string = 0; /* Handle comments and redirection. */ for (p = argv, q = cmd; *p; p++, q++) { if (*p == '"') in_string = !in_string; if (in_string) { *q = *p; continue; } switch (*p) { case '#': *p-- = 0; *q-- = 0; break; case '\\': p++; if (*p == '\n') p++; if (isspace ((unsigned char)*p)) { do { p++; } while (isspace ((unsigned char)*p)); p--; } *q = *p; break; case '<': p = vms_redirect (&ifiledsc, ifile, p); *q = ' '; have_redirection = 1; break; case '>': have_redirection = 1; if (*(p-1) == '2') { q--; if (strncmp (p, ">&1", 3) == 0) { p += 3; strcpy (efile, "sys$output"); efiledsc.dsc$w_length = strlen(efile); efiledsc.dsc$a_pointer = efile; efiledsc.dsc$b_dtype = DSC$K_DTYPE_T; efiledsc.dsc$b_class = DSC$K_CLASS_S; } else { p = vms_redirect (&efiledsc, efile, p); } } else { p = vms_redirect (&ofiledsc, ofile, p); } *q = ' '; break; case '\n': have_newline = 1; default: *q = *p; break; } } *q = *p; while (isspace ((unsigned char)*--q)) *q = '\0'; if (strncmp (cmd, "builtin_", 8) == 0) { child->pid = 270163; child->efn = 0; child->cstatus = 1; DB (DB_JOBS, (_("BUILTIN [%s][%s]\n"), cmd, cmd+8)); p = cmd + 8; if ((*(p) == 'c') && (*(p+1) == 'd') && ((*(p+2) == ' ') || (*(p+2) == '\t'))) { p += 3; while ((*p == ' ') || (*p == '\t')) p++; DB (DB_JOBS, (_("BUILTIN CD %s\n"), p)); if (chdir (p)) return 0; else return 1; } else if ((*(p) == 'r') && (*(p+1) == 'm') && ((*(p+2) == ' ') || (*(p+2) == '\t'))) { int in_arg; /* rm */ p += 3; while ((*p == ' ') || (*p == '\t')) p++; in_arg = 1; DB (DB_JOBS, (_("BUILTIN RM %s\n"), p)); while (*p) { switch (*p) { case ' ': case '\t': if (in_arg) { *p++ = ';'; in_arg = 0; } break; default: break; } p++; } } else { printf(_("Unknown builtin command '%s'\n"), cmd); fflush(stdout); return 0; } } /* Create a *.com file if either the command is too long for lib$spawn, or the command contains a newline, or if redirection is desired. Forcing commands with newlines into DCLs allows to store search lists on user mode logicals. */ if (strlen (cmd) > MAXCMDLEN || (have_redirection != 0) || (have_newline != 0)) { FILE *outfile; char c; char *sep; int alevel = 0; /* apostrophe level */ if (strlen (cmd) == 0) { printf (_("Error, empty command\n")); fflush (stdout); return 0; } outfile = open_tmpfile (&comname, "sys$scratch:CMDXXXXXX.COM"); if (outfile == 0) pfatal_with_name (_("fopen (temporary file)")); if (ifile[0]) { fprintf (outfile, "$ assign/user %s sys$input\n", ifile); DB (DB_JOBS, (_("Redirected input from %s\n"), ifile)); ifiledsc.dsc$w_length = 0; } if (efile[0]) { fprintf (outfile, "$ define sys$error %s\n", efile); DB (DB_JOBS, (_("Redirected error to %s\n"), efile)); efiledsc.dsc$w_length = 0; } if (ofile[0]) { fprintf (outfile, "$ define sys$output %s\n", ofile); DB (DB_JOBS, (_("Redirected output to %s\n"), ofile)); ofiledsc.dsc$w_length = 0; } p = sep = q = cmd; for (c = '\n'; c; c = *q++) { switch (c) { case '\n': /* At a newline, skip any whitespace around a leading $ from the command and issue exactly one $ into the DCL. */ while (isspace ((unsigned char)*p)) p++; if (*p == '$') p++; while (isspace ((unsigned char)*p)) p++; fwrite (p, 1, q - p, outfile); fputc ('$', outfile); fputc (' ', outfile); /* Reset variables. */ p = sep = q; break; /* Nice places for line breaks are after strings, after comma or space and before slash. */ case '"': q = vms_handle_apos (q); sep = q; break; case ',': case ' ': sep = q; break; case '/': case '\0': sep = q - 1; break; default: break; } if (sep - p > 78) { /* Enough stuff for a line. */ fwrite (p, 1, sep - p, outfile); p = sep; if (*sep) { /* The command continues. */ fputc ('-', outfile); } fputc ('\n', outfile); } } fwrite (p, 1, q - p, outfile); fputc ('\n', outfile); fclose (outfile); sprintf (cmd, "$ @%s", comname); DB (DB_JOBS, (_("Executing %s instead\n"), cmd)); } cmddsc.dsc$w_length = strlen(cmd); cmddsc.dsc$a_pointer = cmd; cmddsc.dsc$b_dtype = DSC$K_DTYPE_T; cmddsc.dsc$b_class = DSC$K_CLASS_S; child->efn = 0; while (child->efn < 32 || child->efn > 63) { status = lib$get_ef ((unsigned long *)&child->efn); if (!(status & 1)) return 0; } sys$clref (child->efn); vms_jobsefnmask |= (1 << (child->efn - 32)); /* LIB$SPAWN [command-string] [,input-file] [,output-file] [,flags] [,process-name] [,process-id] [,completion-status-address] [,byte-integer-event-flag-num] [,AST-address] [,varying-AST-argument] [,prompt-string] [,cli] [,table] */ #ifndef DONTWAITFORCHILD /* * Code to make ctrl+c and ctrl+y working. * The problem starts with the synchronous case where after lib$spawn is * called any input will go to the child. But with input re-directed, * both control characters won't make it to any of the programs, neither * the spawning nor to the spawned one. Hence the caller needs to spawn * with CLI$M_NOWAIT to NOT give up the input focus. A sys$waitfr * has to follow to simulate the wanted synchronous behaviour. * The next problem is ctrl+y which isn't caught by the crtl and * therefore isn't converted to SIGQUIT (for a signal handler which is * already established). The only way to catch ctrl+y, is an AST * assigned to the input channel. But ctrl+y handling of DCL needs to be * disabled, otherwise it will handle it. Not to mention the previous * ctrl+y handling of DCL needs to be re-established before make exits. * One more: At the time of LIB$SPAWN signals are blocked. SIGQUIT will * make it to the signal handler after the child "normally" terminates. * This isn't enough. It seems reasonable for simple command lines like * a 'cc foobar.c' spawned in a subprocess but it is unacceptable for * spawning make. Therefore we need to abort the process in the AST. * * Prior to the spawn it is checked if an AST is already set up for * ctrl+y, if not one is set up for a channel to SYS$COMMAND. In general * this will work except if make is run in a batch environment, but there * nobody can press ctrl+y. During the setup the DCL handling of ctrl+y * is disabled and an exit handler is established to re-enable it. * If the user interrupts with ctrl+y, the assigned AST will fire, force * an abort to the subprocess and signal SIGQUIT, which will be caught by * the already established handler and will bring us back to common code. * After the spawn (now /nowait) a sys$waitfr simulates the /wait and * enables the ctrl+y be delivered to this code. And the ctrl+c too, * which the crtl converts to SIGINT and which is caught by the common * signal handler. Because signals were blocked before entering this code * sys$waitfr will always complete and the SIGQUIT will be processed after * it (after termination of the current block, somewhere in common code). * And SIGINT too will be delayed. That is ctrl+c can only abort when the * current command completes. Anyway it's better than nothing :-) */ if (!setupYAstTried) tryToSetupYAst(); status = lib$spawn (&cmddsc, /* cmd-string */ (ifiledsc.dsc$w_length == 0)?0:&ifiledsc, /* input-file */ (ofiledsc.dsc$w_length == 0)?0:&ofiledsc, /* output-file */ &spflags, /* flags */ &pnamedsc, /* proc name */ &child->pid, &child->cstatus, &child->efn, 0, 0, 0, 0, 0); if (status & 1) { pidToAbort= child->pid; status= sys$waitfr (child->efn); pidToAbort= 0; vmsHandleChildTerm(child); } #else status = lib$spawn (&cmddsc, (ifiledsc.dsc$w_length == 0)?0:&ifiledsc, (ofiledsc.dsc$w_length == 0)?0:&ofiledsc, &spflags, &pnamedsc, &child->pid, &child->cstatus, &child->efn, vmsHandleChildTerm, child, 0, 0, 0); #endif if (!(status & 1)) { printf (_("Error spawning, %d\n") ,status); fflush (stdout); switch (status) { case 0x1c: errno = EPROCLIM; break; default: errno = EFAIL; } } if (comname && !ISDB (DB_JOBS)) unlink (comname); return (status & 1); }