static void enter_mode_read_pcap(struct mode *mode) { int ret, fd, fdo = 0; struct pcap_pkthdr phdr; struct sock_fprog bpf_ops; struct tx_stats stats; struct frame_map fm; uint8_t *out; size_t out_len; unsigned long trunced = 0; struct timeval start, end, diff; if (!pcap_ops[mode->pcap]) panic("pcap group not supported!\n"); fd = open_or_die(mode->device_in, O_RDONLY | O_LARGEFILE | O_NOATIME); ret = pcap_ops[mode->pcap]->pull_file_header(fd, &mode->link_type); if (ret) panic("error reading pcap header!\n"); if (pcap_ops[mode->pcap]->prepare_reading_pcap) { ret = pcap_ops[mode->pcap]->prepare_reading_pcap(fd); if (ret) panic("error prepare reading pcap!\n"); } fmemset(&fm, 0, sizeof(fm)); fmemset(&bpf_ops, 0, sizeof(bpf_ops)); fmemset(&stats, 0, sizeof(stats)); bpf_parse_rules(mode->filter, &bpf_ops); dissector_init_all(mode->print_mode); out_len = 64 * 1024; out = xmalloc_aligned(out_len, CO_CACHE_LINE_SIZE); printf("BPF:\n"); bpf_dump_all(&bpf_ops); printf("MD: RD %s ", pcap_ops[mode->pcap]->name); #ifdef _LARGEFILE64_SOURCE printf("lf64 "); #endif ioprio_print(); printf("\n"); if (mode->device_out) { fdo = open_or_die_m(mode->device_out, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR); } gettimeofday(&start, NULL); while (likely(sigint == 0)) { do { memset(&phdr, 0, sizeof(phdr)); ret = pcap_ops[mode->pcap]->read_pcap_pkt(fd, &phdr, out, out_len); if (unlikely(ret < 0)) goto out; if (unlikely(phdr.len == 0)) { trunced++; continue; } if (unlikely(phdr.len > out_len)) { phdr.len = out_len; trunced++; } } while (mode->filter && !bpf_run_filter(&bpf_ops, out, phdr.len)); pcap_pkthdr_to_tpacket_hdr(&phdr, &fm.tp_h); stats.tx_bytes += fm.tp_h.tp_len; stats.tx_packets++; show_frame_hdr(&fm, mode->print_mode, RING_MODE_EGRESS); dissector_entry_point(out, fm.tp_h.tp_snaplen, mode->link_type, mode->print_mode); if (mode->device_out) { int i = 0; char bout[80]; slprintf(bout, sizeof(bout), "{\n "); write_or_die(fdo, bout, strlen(bout)); while (i < fm.tp_h.tp_snaplen) { slprintf(bout, sizeof(bout), "0x%02x, ", out[i]); write_or_die(fdo, bout, strlen(bout)); i++; if (i % 10 == 0) { slprintf(bout, sizeof(bout), "\n", out[i]); write_or_die(fdo, bout, strlen(bout)); if (i < fm.tp_h.tp_snaplen) { slprintf(bout, sizeof(bout), " ", out[i]); write_or_die(fdo, bout, strlen(bout)); } } } if (i % 10 != 0) { slprintf(bout, sizeof(bout), "\n"); write_or_die(fdo, bout, strlen(bout)); } slprintf(bout, sizeof(bout), "}\n\n"); write_or_die(fdo, bout, strlen(bout)); } if (frame_cnt_max != 0 && stats.tx_packets >= frame_cnt_max) { sigint = 1; break; } } out: gettimeofday(&end, NULL); diff = tv_subtract(end, start); fflush(stdout); printf("\n"); printf("\r%12lu frames outgoing\n", stats.tx_packets); printf("\r%12lu frames truncated (larger than mtu)\n", trunced); printf("\r%12lu bytes outgoing\n", stats.tx_bytes); printf("\r%12lu sec, %lu usec in total\n", diff.tv_sec, diff.tv_usec); xfree(out); bpf_release(&bpf_ops); dissector_cleanup_all(); if (pcap_ops[mode->pcap]->prepare_close_pcap) pcap_ops[mode->pcap]->prepare_close_pcap(fd, PCAP_MODE_READ); close(fd); if (mode->device_out) close(fdo); }
static void create_pack_file(void) { struct child_process pack_objects = CHILD_PROCESS_INIT; char data[8193], progress[128]; char abort_msg[] = "aborting due to possible repository " "corruption on the remote side."; int buffered = -1; ssize_t sz; int i; FILE *pipe_fd; if (!pack_objects_hook) pack_objects.git_cmd = 1; else { argv_array_push(&pack_objects.args, pack_objects_hook); argv_array_push(&pack_objects.args, "git"); pack_objects.use_shell = 1; } if (shallow_nr) { argv_array_push(&pack_objects.args, "--shallow-file"); argv_array_push(&pack_objects.args, ""); } argv_array_push(&pack_objects.args, "pack-objects"); argv_array_push(&pack_objects.args, "--revs"); if (use_thin_pack) argv_array_push(&pack_objects.args, "--thin"); argv_array_push(&pack_objects.args, "--stdout"); if (shallow_nr) argv_array_push(&pack_objects.args, "--shallow"); if (!no_progress) argv_array_push(&pack_objects.args, "--progress"); if (use_ofs_delta) argv_array_push(&pack_objects.args, "--delta-base-offset"); if (use_include_tag) argv_array_push(&pack_objects.args, "--include-tag"); if (filter_options.filter_spec) { if (pack_objects.use_shell) { struct strbuf buf = STRBUF_INIT; sq_quote_buf(&buf, filter_options.filter_spec); argv_array_pushf(&pack_objects.args, "--filter=%s", buf.buf); strbuf_release(&buf); } else { argv_array_pushf(&pack_objects.args, "--filter=%s", filter_options.filter_spec); } } pack_objects.in = -1; pack_objects.out = -1; pack_objects.err = -1; if (start_command(&pack_objects)) die("git upload-pack: unable to fork git-pack-objects"); pipe_fd = xfdopen(pack_objects.in, "w"); if (shallow_nr) for_each_commit_graft(write_one_shallow, pipe_fd); for (i = 0; i < want_obj.nr; i++) fprintf(pipe_fd, "%s\n", oid_to_hex(&want_obj.objects[i].item->oid)); fprintf(pipe_fd, "--not\n"); for (i = 0; i < have_obj.nr; i++) fprintf(pipe_fd, "%s\n", oid_to_hex(&have_obj.objects[i].item->oid)); for (i = 0; i < extra_edge_obj.nr; i++) fprintf(pipe_fd, "%s\n", oid_to_hex(&extra_edge_obj.objects[i].item->oid)); fprintf(pipe_fd, "\n"); fflush(pipe_fd); fclose(pipe_fd); /* We read from pack_objects.err to capture stderr output for * progress bar, and pack_objects.out to capture the pack data. */ while (1) { struct pollfd pfd[2]; int pe, pu, pollsize; int ret; reset_timeout(); pollsize = 0; pe = pu = -1; if (0 <= pack_objects.out) { pfd[pollsize].fd = pack_objects.out; pfd[pollsize].events = POLLIN; pu = pollsize; pollsize++; } if (0 <= pack_objects.err) { pfd[pollsize].fd = pack_objects.err; pfd[pollsize].events = POLLIN; pe = pollsize; pollsize++; } if (!pollsize) break; ret = poll(pfd, pollsize, keepalive < 0 ? -1 : 1000 * keepalive); if (ret < 0) { if (errno != EINTR) { error_errno("poll failed, resuming"); sleep(1); } continue; } if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { /* Status ready; we ship that in the side-band * or dump to the standard error. */ sz = xread(pack_objects.err, progress, sizeof(progress)); if (0 < sz) send_client_data(2, progress, sz); else if (sz == 0) { close(pack_objects.err); pack_objects.err = -1; } else goto fail; /* give priority to status messages */ continue; } if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { /* Data ready; we keep the last byte to ourselves * in case we detect broken rev-list, so that we * can leave the stream corrupted. This is * unfortunate -- unpack-objects would happily * accept a valid packdata with trailing garbage, * so appending garbage after we pass all the * pack data is not good enough to signal * breakage to downstream. */ char *cp = data; ssize_t outsz = 0; if (0 <= buffered) { *cp++ = buffered; outsz++; } sz = xread(pack_objects.out, cp, sizeof(data) - outsz); if (0 < sz) ; else if (sz == 0) { close(pack_objects.out); pack_objects.out = -1; } else goto fail; sz += outsz; if (1 < sz) { buffered = data[sz-1] & 0xFF; sz--; } else buffered = -1; send_client_data(1, data, sz); } /* * We hit the keepalive timeout without saying anything; send * an empty message on the data sideband just to let the other * side know we're still working on it, but don't have any data * yet. * * If we don't have a sideband channel, there's no room in the * protocol to say anything, so those clients are just out of * luck. */ if (!ret && use_sideband) { static const char buf[] = "0005\1"; write_or_die(1, buf, 5); } } if (finish_command(&pack_objects)) { error("git upload-pack: git-pack-objects died with error."); goto fail; } /* flush the data */ if (0 <= buffered) { data[0] = buffered; send_client_data(1, data, 1); fprintf(stderr, "flushed.\n"); } if (use_sideband) packet_flush(1); return; fail: send_client_data(3, abort_msg, sizeof(abort_msg)); die("git upload-pack: %s", abort_msg); }
int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { static struct lock_file lock; int bundle_fd = -1; int bundle_to_stdout; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(6 * sizeof(const char *)); int i, ref_count = 0; char buffer[1024]; struct rev_info revs; struct child_process rls; FILE *rls_fout; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) bundle_fd = 1; else bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; init_revisions(&revs, NULL); /* write prerequisites */ memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *)); argv_boundary[0] = "rev-list"; argv_boundary[1] = "--boundary"; argv_boundary[2] = "--pretty=oneline"; argv_boundary[argc + 2] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_boundary; rls.out = -1; rls.git_cmd = 1; if (start_command(&rls)) return -1; rls_fout = xfdopen(rls.out, "r"); while (fgets(buffer, sizeof(buffer), rls_fout)) { unsigned char sha1[20]; if (buffer[0] == '-') { write_or_die(bundle_fd, buffer, strlen(buffer)); if (!get_sha1_hex(buffer + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; add_pending_object(&revs, object, buffer); } } else if (!get_sha1_hex(buffer, sha1)) { struct object *object = parse_object(sha1); object->flags |= SHOWN; } } fclose(rls_fout); if (finish_command(&rls)) return error("rev-list died"); /* write references */ argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) return error("unrecognized argument: %s'", argv[1]); object_array_remove_duplicates(&revs.pending); for (i = 0; i < revs.pending.nr; i++) { struct object_array_entry *e = revs.pending.objects + i; unsigned char sha1[20]; char *ref; const char *display_ref; int flag; if (e->item->flags & UNINTERESTING) continue; if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1) continue; if (!resolve_ref(e->name, sha1, 1, &flag)) flag = 0; display_ref = (flag & REF_ISSYMREF) ? e->name : ref; if (e->item->type == OBJ_TAG && !is_tag_in_date_range(e->item, &revs)) { e->item->flags |= UNINTERESTING; continue; } /* * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. * * Non commit objects such as tags and blobs do not have * this issue as they are not affected by those extra * constraints. */ if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning("ref '%s' is excluded by the rev-list options", e->name); free(ref); continue; } /* * If you run "git bundle create bndl v1.0..v2.0", the * name of the positive ref is "v2.0" but that is the * commit that is referenced by the tag, and not the tag * itself. */ if (hashcmp(sha1, e->item->sha1)) { /* * Is this the positive end of a range expressed * in terms of a tag (e.g. v2.0 from the range * "v1.0..v2.0")? */ struct commit *one = lookup_commit_reference(sha1); struct object *obj; if (e->item == &(one->object)) { /* * Need to include e->name as an * independent ref to the pack-objects * input, so that the tag is included * in the output; otherwise we would * end up triggering "empty bundle" * error. */ obj = parse_object(sha1); obj->flags |= SHOWN; add_pending_object(&revs, obj, e->name); } free(ref); continue; } ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); write_or_die(bundle_fd, display_ref, strlen(display_ref)); write_or_die(bundle_fd, "\n", 1); free(ref); } if (!ref_count) die ("Refusing to create empty bundle."); /* end header */ write_or_die(bundle_fd, "\n", 1); /* write pack */ argv_pack[0] = "pack-objects"; argv_pack[1] = "--all-progress-implied"; argv_pack[2] = "--stdout"; argv_pack[3] = "--thin"; argv_pack[4] = "--delta-base-offset"; argv_pack[5] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_pack; rls.in = -1; rls.out = bundle_fd; rls.git_cmd = 1; if (start_command(&rls)) return error("Could not spawn pack-objects"); /* * start_command closed bundle_fd if it was > 1 * so set the lock fd to -1 so commit_lock_file() * won't fail trying to close it. */ lock.fd = -1; for (i = 0; i < revs.pending.nr; i++) { struct object *object = revs.pending.objects[i].item; if (object->flags & UNINTERESTING) write_or_die(rls.in, "^", 1); write_or_die(rls.in, sha1_to_hex(object->sha1), 40); write_or_die(rls.in, "\n", 1); } close(rls.in); if (finish_command(&rls)) return error ("pack-objects died"); if (!bundle_to_stdout) { if (commit_lock_file(&lock)) die_errno("cannot create '%s'", path); } return 0; }
static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size) { /* the parser in tag.c is useless here. */ const char *endp = buf + size; const char *cp = buf; while (cp < endp) { char c = *cp++; if (c != '\n') continue; if (7 <= endp - cp && !memcmp("tagger ", cp, 7)) { const char *tagger = cp; /* Found the tagger line. Copy out the contents * of the buffer so far. */ write_or_die(1, buf, cp - buf); /* * Do something intelligent, like pretty-printing * the date. */ while (cp < endp) { if (*cp++ == '\n') { /* tagger to cp is a line * that has ident and time. */ const char *sp = tagger; char *ep; unsigned long date; long tz; while (sp < cp && *sp != '>') sp++; if (sp == cp) { /* give up */ write_or_die(1, tagger, cp - tagger); break; } while (sp < cp && !('0' <= *sp && *sp <= '9')) sp++; write_or_die(1, tagger, sp - tagger); date = strtoul(sp, &ep, 10); tz = strtol(ep, NULL, 10); sp = show_date(date, tz, 0); write_or_die(1, sp, strlen(sp)); xwrite(1, "\n", 1); break; } } break; } if (cp < endp && *cp == '\n') /* end of header */ break; } /* At this point, we have copied out the header up to the end of * the tagger line and cp points at one past \n. It could be the * next header line after the tagger line, or it could be another * \n that marks the end of the headers. We need to copy out the * remainder as is. */ if (cp < endp) write_or_die(1, cp, endp - cp); }
int send_pack(struct send_pack_args *args, int fd[], struct child_process *conn, struct ref *remote_refs, struct sha1_array *extra_have) { int in = fd[0]; int out = fd[1]; struct strbuf req_buf = STRBUF_INIT; struct strbuf cap_buf = STRBUF_INIT; struct ref *ref; int need_pack_data = 0; int allow_deleting_refs = 0; int status_report = 0; int use_sideband = 0; int quiet_supported = 0; int agent_supported = 0; int use_atomic = 0; int atomic_supported = 0; int use_push_options = 0; int push_options_supported = 0; unsigned cmds_sent = 0; int ret; struct async demux; const char *push_cert_nonce = NULL; git_config(send_pack_config, NULL); /* Does the other end support the reporting? */ if (server_supports("report-status")) status_report = 1; if (server_supports("delete-refs")) allow_deleting_refs = 1; if (server_supports("ofs-delta")) args->use_ofs_delta = 1; if (config_use_sideband && server_supports("side-band-64k")) use_sideband = 1; if (server_supports("quiet")) quiet_supported = 1; if (server_supports("agent")) agent_supported = 1; if (server_supports("no-thin")) args->use_thin_pack = 0; if (server_supports("atomic")) atomic_supported = 1; if (server_supports("push-options")) push_options_supported = 1; if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) { int len; push_cert_nonce = server_feature_value("push-cert", &len); if (push_cert_nonce) { reject_invalid_nonce(push_cert_nonce, len); push_cert_nonce = xmemdupz(push_cert_nonce, len); } else if (args->push_cert == SEND_PACK_PUSH_CERT_ALWAYS) { die(_("the receiving end does not support --signed push")); } else if (args->push_cert == SEND_PACK_PUSH_CERT_IF_ASKED) { warning(_("not sending a push certificate since the" " receiving end does not support --signed" " push")); } } if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" "Perhaps you should specify a branch such as 'master'.\n"); return 0; } if (args->atomic && !atomic_supported) die(_("the receiving end does not support --atomic push")); use_atomic = atomic_supported && args->atomic; if (args->push_options && !push_options_supported) die(_("the receiving end does not support push options")); use_push_options = push_options_supported && args->push_options; if (status_report) strbuf_addstr(&cap_buf, " report-status"); if (use_sideband) strbuf_addstr(&cap_buf, " side-band-64k"); if (quiet_supported && (args->quiet || !args->progress)) strbuf_addstr(&cap_buf, " quiet"); if (use_atomic) strbuf_addstr(&cap_buf, " atomic"); if (use_push_options) strbuf_addstr(&cap_buf, " push-options"); if (agent_supported) strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized()); /* * NEEDSWORK: why does delete-refs have to be so specific to * send-pack machinery that set_ref_status_for_push() cannot * set this bit for us??? */ for (ref = remote_refs; ref; ref = ref->next) if (ref->deletion && !allow_deleting_refs) ref->status = REF_STATUS_REJECT_NODELETE; if (!args->dry_run) advertise_shallow_grafts_buf(&req_buf); if (!args->dry_run && push_cert_nonce) cmds_sent = generate_push_cert(&req_buf, remote_refs, args, cap_buf.buf, push_cert_nonce); /* * Clear the status for each ref and see if we need to send * the pack data. */ for (ref = remote_refs; ref; ref = ref->next) { switch (check_to_send_update(ref, args)) { case 0: /* no error */ break; case CHECK_REF_STATUS_REJECTED: /* * When we know the server would reject a ref update if * we were to send it and we're trying to send the refs * atomically, abort the whole operation. */ if (use_atomic) return atomic_push_failure(args, remote_refs, ref); /* Fallthrough for non atomic case. */ default: continue; } if (!ref->deletion) need_pack_data = 1; if (args->dry_run || !status_report) ref->status = REF_STATUS_OK; else ref->status = REF_STATUS_EXPECTING_REPORT; } /* * Finally, tell the other end! */ for (ref = remote_refs; ref; ref = ref->next) { char *old_hex, *new_hex; if (args->dry_run || push_cert_nonce) continue; if (check_to_send_update(ref, args) < 0) continue; old_hex = oid_to_hex(&ref->old_oid); new_hex = oid_to_hex(&ref->new_oid); if (!cmds_sent) { packet_buf_write(&req_buf, "%s %s %s%c%s", old_hex, new_hex, ref->name, 0, cap_buf.buf); cmds_sent = 1; } else { packet_buf_write(&req_buf, "%s %s %s", old_hex, new_hex, ref->name); } } if (args->stateless_rpc) { if (!args->dry_run && (cmds_sent || is_repository_shallow())) { packet_buf_flush(&req_buf); send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX); } } else { write_or_die(out, req_buf.buf, req_buf.len); packet_flush(out); } strbuf_release(&req_buf); strbuf_release(&cap_buf); if (use_push_options) { struct string_list_item *item; struct strbuf sb = STRBUF_INIT; for_each_string_list_item(item, args->push_options) packet_buf_write(&sb, "%s", item->string); write_or_die(out, sb.buf, sb.len); packet_flush(out); strbuf_release(&sb); } if (use_sideband && cmds_sent) { memset(&demux, 0, sizeof(demux)); demux.proc = sideband_demux; demux.data = fd; demux.out = -1; demux.isolate_sigpipe = 1; if (start_async(&demux)) die("send-pack: unable to fork off sideband demultiplexer"); in = demux.out; } if (need_pack_data && cmds_sent) { if (pack_objects(out, remote_refs, extra_have, args) < 0) { for (ref = remote_refs; ref; ref = ref->next) ref->status = REF_STATUS_NONE; if (args->stateless_rpc) close(out); if (git_connection_is_socket(conn)) shutdown(fd[0], SHUT_WR); if (use_sideband) { close(demux.out); finish_async(&demux); } fd[1] = -1; return -1; } if (!args->stateless_rpc) /* Closed by pack_objects() via start_command() */ fd[1] = -1; } if (args->stateless_rpc && cmds_sent) packet_flush(out); if (status_report && cmds_sent) ret = receive_status(in, remote_refs); else ret = 0; if (args->stateless_rpc) packet_flush(out); if (use_sideband && cmds_sent) { close(demux.out); if (finish_async(&demux)) { error("error in sideband demultiplexer"); ret = -1; } } if (ret < 0) return ret; if (args->porcelain) return 0; for (ref = remote_refs; ref; ref = ref->next) { switch (ref->status) { case REF_STATUS_NONE: case REF_STATUS_UPTODATE: case REF_STATUS_OK: break; default: return -1; } } return 0; }
int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { static struct lock_file lock; int bundle_fd = -1; int bundle_to_stdout; int ref_count = 0; struct rev_info revs; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) bundle_fd = 1; else { bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); /* * write_pack_data() will close the fd passed to it, * but commit_lock_file() will also try to close the * lockfile's fd. So make a copy of the file * descriptor to avoid trying to close it twice. */ bundle_fd = dup(bundle_fd); if (bundle_fd < 0) die_errno("unable to dup file descriptor"); } /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; init_revisions(&revs, NULL); /* write prerequisites */ if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv)) return -1; argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) return error(_("unrecognized argument: %s"), argv[1]); object_array_remove_duplicates(&revs.pending); ref_count = write_bundle_refs(bundle_fd, &revs); if (!ref_count) die(_("Refusing to create empty bundle.")); else if (ref_count < 0) return -1; /* write pack */ if (write_pack_data(bundle_fd, &revs)) return -1; if (!bundle_to_stdout) { if (commit_lock_file(&lock)) die_errno(_("cannot create '%s'"), path); } return 0; }
static void end_headers(void) { write_or_die(1, "\r\n", 2); }
int recv_sideband(const char *me, int in_stream, int out) { unsigned pf = strlen(PREFIX); unsigned sf; char buf[LARGE_PACKET_MAX + 2*FIX_SIZE]; char *suffix, *term; int skip_pf = 0; memcpy(buf, PREFIX, pf); term = getenv("TERM"); if (term && strcmp(term, "dumb")) suffix = ANSI_SUFFIX; else suffix = DUMB_SUFFIX; sf = strlen(suffix); while (1) { int band, len; len = packet_read(in_stream, NULL, NULL, buf + pf, LARGE_PACKET_MAX, 0); if (len == 0) break; if (len < 1) { fprintf(stderr, "%s: protocol error: no band designator\n", me); return SIDEBAND_PROTOCOL_ERROR; } band = buf[pf] & 0xff; len--; switch (band) { case 3: buf[pf] = ' '; buf[pf+1+len] = '\0'; fprintf(stderr, "%s\n", buf); return SIDEBAND_REMOTE_ERROR; case 2: buf[pf] = ' '; do { char *b = buf; int brk = 0; /* * If the last buffer didn't end with a line * break then we should not print a prefix * this time around. */ if (skip_pf) { b += pf+1; } else { len += pf+1; brk += pf+1; } /* Look for a line break. */ for (;;) { brk++; if (brk > len) { brk = 0; break; } if (b[brk-1] == '\n' || b[brk-1] == '\r') break; } /* * Let's insert a suffix to clear the end * of the screen line if a line break was * found. Also, if we don't skip the * prefix, then a non-empty string must be * present too. */ if (brk > (skip_pf ? 0 : (pf+1 + 1))) { char save[FIX_SIZE]; memcpy(save, b + brk, sf); b[brk + sf - 1] = b[brk - 1]; memcpy(b + brk - 1, suffix, sf); fprintf(stderr, "%.*s", brk + sf, b); memcpy(b + brk, save, sf); len -= brk; } else { int l = brk ? brk : len; fprintf(stderr, "%.*s", l, b); len -= l; } skip_pf = !brk; memmove(buf + pf+1, b + brk, len); } while (len); continue; case 1: write_or_die(out, buf + pf+1, len); continue; default: fprintf(stderr, "%s: protocol error: bad band #%d\n", me, band); return SIDEBAND_PROTOCOL_ERROR; } } return 0; }
/* * Write out bundle refs based on the tips already * parsed into revs.pending. As a side effect, may * manipulate revs.pending to include additional * necessary objects (like tags). * * Returns the number of refs written, or negative * on error. */ static int write_bundle_refs(int bundle_fd, struct rev_info *revs) { int i; int ref_count = 0; for (i = 0; i < revs->pending.nr; i++) { struct object_array_entry *e = revs->pending.objects + i; unsigned char sha1[20]; char *ref; const char *display_ref; int flag; if (e->item->flags & UNINTERESTING) continue; if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1) goto skip_write_ref; if (read_ref_full(e->name, RESOLVE_REF_READING, sha1, &flag)) flag = 0; display_ref = (flag & REF_ISSYMREF) ? e->name : ref; if (e->item->type == OBJ_TAG && !is_tag_in_date_range(e->item, revs)) { e->item->flags |= UNINTERESTING; goto skip_write_ref; } /* * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. * * Non commit objects such as tags and blobs do not have * this issue as they are not affected by those extra * constraints. */ if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning(_("ref '%s' is excluded by the rev-list options"), e->name); goto skip_write_ref; } /* * If you run "git bundle create bndl v1.0..v2.0", the * name of the positive ref is "v2.0" but that is the * commit that is referenced by the tag, and not the tag * itself. */ if (hashcmp(sha1, e->item->sha1)) { /* * Is this the positive end of a range expressed * in terms of a tag (e.g. v2.0 from the range * "v1.0..v2.0")? */ struct commit *one = lookup_commit_reference(sha1); struct object *obj; if (e->item == &(one->object)) { /* * Need to include e->name as an * independent ref to the pack-objects * input, so that the tag is included * in the output; otherwise we would * end up triggering "empty bundle" * error. */ obj = parse_object_or_die(sha1, e->name); obj->flags |= SHOWN; add_pending_object(revs, obj, e->name); } goto skip_write_ref; } ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); write_or_die(bundle_fd, display_ref, strlen(display_ref)); write_or_die(bundle_fd, "\n", 1); skip_write_ref: free(ref); } /* end header */ write_or_die(bundle_fd, "\n", 1); return ref_count; }
static void print_line(struct strbuf *buf) { strbuf_addch(buf, '\n'); write_or_die(get_lock_file_fd(&credential_lock), buf->buf, buf->len); }
static int fetch_git(struct discovery *heads, int nr_heads, struct ref **to_fetch) { struct rpc_state rpc; struct strbuf preamble = STRBUF_INIT; int i, err; struct argv_array args = ARGV_ARRAY_INIT; argv_array_pushl(&args, "fetch-pack", "--stateless-rpc", "--stdin", "--lock-pack", NULL); if (options.followtags) argv_array_push(&args, "--include-tag"); if (options.thin) argv_array_push(&args, "--thin"); if (options.verbosity >= 3) argv_array_pushl(&args, "-v", "-v", NULL); if (options.check_self_contained_and_connected) argv_array_push(&args, "--check-self-contained-and-connected"); if (options.cloning) argv_array_push(&args, "--cloning"); if (options.update_shallow) argv_array_push(&args, "--update-shallow"); if (!options.progress) argv_array_push(&args, "--no-progress"); if (options.depth) argv_array_pushf(&args, "--depth=%lu", options.depth); if (options.deepen_since) argv_array_pushf(&args, "--shallow-since=%s", options.deepen_since); for (i = 0; i < options.deepen_not.nr; i++) argv_array_pushf(&args, "--shallow-exclude=%s", options.deepen_not.items[i].string); if (options.deepen_relative && options.depth) argv_array_push(&args, "--deepen-relative"); if (options.from_promisor) argv_array_push(&args, "--from-promisor"); if (options.no_dependents) argv_array_push(&args, "--no-dependents"); if (options.filter) argv_array_pushf(&args, "--filter=%s", options.filter); argv_array_push(&args, url.buf); for (i = 0; i < nr_heads; i++) { struct ref *ref = to_fetch[i]; if (!*ref->name) die("cannot fetch by sha1 over smart http"); packet_buf_write(&preamble, "%s %s\n", oid_to_hex(&ref->old_oid), ref->name); } packet_buf_flush(&preamble); memset(&rpc, 0, sizeof(rpc)); rpc.service_name = "git-upload-pack", rpc.argv = args.argv; rpc.stdin_preamble = &preamble; rpc.gzip_request = 1; err = rpc_service(&rpc, heads); if (rpc.result.len) write_or_die(1, rpc.result.buf, rpc.result.len); strbuf_release(&rpc.result); strbuf_release(&preamble); argv_array_clear(&args); return err; }
static int fetch_git(struct discovery *heads, int nr_heads, struct ref **to_fetch) { struct rpc_state rpc; struct strbuf preamble = STRBUF_INIT; char *depth_arg = NULL; int argc = 0, i, err; const char *argv[17]; argv[argc++] = "fetch-pack"; argv[argc++] = "--stateless-rpc"; argv[argc++] = "--stdin"; argv[argc++] = "--lock-pack"; if (options.followtags) argv[argc++] = "--include-tag"; if (options.thin) argv[argc++] = "--thin"; if (options.verbosity >= 3) { argv[argc++] = "-v"; argv[argc++] = "-v"; } if (options.check_self_contained_and_connected) argv[argc++] = "--check-self-contained-and-connected"; if (options.cloning) argv[argc++] = "--cloning"; if (options.update_shallow) argv[argc++] = "--update-shallow"; if (!options.progress) argv[argc++] = "--no-progress"; if (options.depth) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "--depth=%lu", options.depth); depth_arg = strbuf_detach(&buf, NULL); argv[argc++] = depth_arg; } argv[argc++] = url.buf; argv[argc++] = NULL; for (i = 0; i < nr_heads; i++) { struct ref *ref = to_fetch[i]; if (!*ref->name) die("cannot fetch by sha1 over smart http"); packet_buf_write(&preamble, "%s %s\n", oid_to_hex(&ref->old_oid), ref->name); } packet_buf_flush(&preamble); memset(&rpc, 0, sizeof(rpc)); rpc.service_name = "git-upload-pack", rpc.argv = argv; rpc.stdin_preamble = &preamble; rpc.gzip_request = 1; err = rpc_service(&rpc, heads); if (rpc.result.len) write_or_die(1, rpc.result.buf, rpc.result.len); strbuf_release(&rpc.result); strbuf_release(&preamble); free(depth_arg); return err; }
static void create_tag(const unsigned char *object, const char *tag, struct strbuf *buf, struct create_tag_options *opt, unsigned char *prev, unsigned char *result) { enum object_type type; char header_buf[1024]; int header_len; char *path = NULL; type = sha1_object_info(object, NULL); if (type <= OBJ_NONE) die(_("bad object type.")); header_len = snprintf(header_buf, sizeof(header_buf), "object %s\n" "type %s\n" "tag %s\n" "tagger %s\n\n", sha1_to_hex(object), typename(type), tag, git_committer_info(IDENT_STRICT)); if (header_len > sizeof(header_buf) - 1) die(_("tag header too big.")); if (!opt->message_given) { int fd; /* write the template message before editing: */ path = git_pathdup("TAG_EDITMSG"); fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (fd < 0) die_errno(_("could not create file '%s'"), path); if (!is_null_sha1(prev)) { write_tag_body(fd, prev); } else { struct strbuf buf = STRBUF_INIT; strbuf_addch(&buf, '\n'); if (opt->cleanup_mode == CLEANUP_ALL) strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char); else strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char); write_or_die(fd, buf.buf, buf.len); strbuf_release(&buf); } close(fd); if (launch_editor(path, buf, NULL)) { fprintf(stderr, _("Please supply the message using either -m or -F option.\n")); exit(1); } } if (opt->cleanup_mode != CLEANUP_NONE) strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL); if (!opt->message_given && !buf->len) die(_("no tag message?")); strbuf_insert(buf, 0, header_buf, header_len); if (build_tag_object(buf, opt->sign, result) < 0) { if (path) fprintf(stderr, _("The tag message has been left in %s\n"), path); exit(128); } if (path) { unlink_or_warn(path); free(path); } }
static void create_note(const unsigned char *object, struct msg_arg *msg, int append_only, const unsigned char *prev, unsigned char *result) { char *path = NULL; if (msg->use_editor || !msg->given) { int fd; struct strbuf buf = STRBUF_INIT; /* write the template message before editing: */ path = git_pathdup("NOTES_EDITMSG"); fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (fd < 0) die_errno(_("could not create file '%s'"), path); if (msg->given) write_or_die(fd, msg->buf.buf, msg->buf.len); else if (prev && !append_only) write_note_data(fd, prev); strbuf_addch(&buf, '\n'); strbuf_add_commented_lines(&buf, note_template, strlen(note_template)); strbuf_addch(&buf, '\n'); write_or_die(fd, buf.buf, buf.len); write_commented_object(fd, object); close(fd); strbuf_release(&buf); strbuf_reset(&(msg->buf)); if (launch_editor(path, &(msg->buf), NULL)) { die(_("Please supply the note contents using either -m" \ " or -F option")); } stripspace(&(msg->buf), 1); } if (prev && append_only) { /* Append buf to previous note contents */ unsigned long size; enum object_type type; char *prev_buf = read_sha1_file(prev, &type, &size); strbuf_grow(&(msg->buf), size + 1); if (msg->buf.len && prev_buf && size) strbuf_insert(&(msg->buf), 0, "\n", 1); if (prev_buf && size) strbuf_insert(&(msg->buf), 0, prev_buf, size); free(prev_buf); } if (!msg->buf.len) { fprintf(stderr, _("Removing note for object %s\n"), sha1_to_hex(object)); hashclr(result); } else { if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) { error(_("unable to write note object")); if (path) error(_("The note contents has been left in %s"), path); exit(128); } } if (path) { unlink_or_warn(path); free(path); } }
static int test_if_untracked_cache_is_supported(void) { struct stat st; struct stat_data base; int fd, ret = 0; strbuf_addstr(&mtime_dir, "mtime-test-XXXXXX"); if (!mkdtemp(mtime_dir.buf)) die_errno("Could not make temporary directory"); fprintf(stderr, _("Testing mtime in '%s' "), xgetcwd()); atexit(remove_test_directory); xstat_mtime_dir(&st); fill_stat_data(&base, &st); fputc('.', stderr); avoid_racy(); fd = create_file("newfile"); xstat_mtime_dir(&st); if (!match_stat_data(&base, &st)) { close(fd); fputc('\n', stderr); fprintf_ln(stderr,_("directory stat info does not " "change after adding a new file")); goto done; } fill_stat_data(&base, &st); fputc('.', stderr); avoid_racy(); xmkdir("new-dir"); xstat_mtime_dir(&st); if (!match_stat_data(&base, &st)) { close(fd); fputc('\n', stderr); fprintf_ln(stderr, _("directory stat info does not change " "after adding a new directory")); goto done; } fill_stat_data(&base, &st); fputc('.', stderr); avoid_racy(); write_or_die(fd, "data", 4); close(fd); xstat_mtime_dir(&st); if (match_stat_data(&base, &st)) { fputc('\n', stderr); fprintf_ln(stderr, _("directory stat info changes " "after updating a file")); goto done; } fputc('.', stderr); avoid_racy(); close(create_file("new-dir/new")); xstat_mtime_dir(&st); if (match_stat_data(&base, &st)) { fputc('\n', stderr); fprintf_ln(stderr, _("directory stat info changes after " "adding a file inside subdirectory")); goto done; } fputc('.', stderr); avoid_racy(); xunlink("newfile"); xstat_mtime_dir(&st); if (!match_stat_data(&base, &st)) { fputc('\n', stderr); fprintf_ln(stderr, _("directory stat info does not " "change after deleting a file")); goto done; } fill_stat_data(&base, &st); fputc('.', stderr); avoid_racy(); xunlink("new-dir/new"); xrmdir("new-dir"); xstat_mtime_dir(&st); if (!match_stat_data(&base, &st)) { fputc('\n', stderr); fprintf_ln(stderr, _("directory stat info does not " "change after deleting a directory")); goto done; } if (rmdir(mtime_dir.buf)) die_errno(_("failed to delete directory %s"), mtime_dir.buf); fprintf_ln(stderr, _(" OK")); ret = 1; done: strbuf_release(&mtime_dir); return ret; }
int recv_sideband(const char *me, int in_stream, int out) { const char *suffix; char buf[LARGE_PACKET_MAX + 1]; struct strbuf outbuf = STRBUF_INIT; int retval = 0; if (isatty(2) && !is_terminal_dumb()) suffix = ANSI_SUFFIX; else suffix = DUMB_SUFFIX; while (!retval) { const char *b, *brk; int band, len; len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX, 0); if (len == 0) break; if (len < 1) { strbuf_addf(&outbuf, "%s%s: protocol error: no band designator", outbuf.len ? "\n" : "", me); retval = SIDEBAND_PROTOCOL_ERROR; break; } band = buf[0] & 0xff; buf[len] = '\0'; len--; switch (band) { case 3: strbuf_addf(&outbuf, "%s%s", outbuf.len ? "\n" : "", DISPLAY_PREFIX); maybe_colorize_sideband(&outbuf, buf + 1, len); retval = SIDEBAND_REMOTE_ERROR; break; case 2: b = buf + 1; /* * Append a suffix to each nonempty line to clear the * end of the screen line. * * The output is accumulated in a buffer and * each line is printed to stderr using * write(2) to ensure inter-process atomicity. */ while ((brk = strpbrk(b, "\n\r"))) { int linelen = brk - b; if (!outbuf.len) strbuf_addstr(&outbuf, DISPLAY_PREFIX); if (linelen > 0) { maybe_colorize_sideband(&outbuf, b, linelen); strbuf_addstr(&outbuf, suffix); } strbuf_addch(&outbuf, *brk); xwrite(2, outbuf.buf, outbuf.len); strbuf_reset(&outbuf); b = brk + 1; } if (*b) { strbuf_addstr(&outbuf, outbuf.len ? "" : DISPLAY_PREFIX); maybe_colorize_sideband(&outbuf, b, strlen(b)); } break; case 1: write_or_die(out, buf + 1, len); break; default: strbuf_addf(&outbuf, "%s%s: protocol error: bad band #%d", outbuf.len ? "\n" : "", me, band); retval = SIDEBAND_PROTOCOL_ERROR; break; } } if (outbuf.len) { strbuf_addch(&outbuf, '\n'); xwrite(2, outbuf.buf, outbuf.len); } strbuf_release(&outbuf); return retval; }
static int rpc_service(struct rpc_state *rpc, struct discovery *heads) { const char *svc = rpc->service_name; struct strbuf buf = STRBUF_INIT; struct strbuf *preamble = rpc->stdin_preamble; struct child_process client; int err = 0; memset(&client, 0, sizeof(client)); client.in = -1; client.out = -1; client.git_cmd = 1; client.argv = rpc->argv; if (start_command(&client)) exit(1); if (preamble) write_or_die(client.in, preamble->buf, preamble->len); if (heads) write_or_die(client.in, heads->buf, heads->len); rpc->alloc = http_post_buffer; rpc->buf = xmalloc(rpc->alloc); rpc->in = client.in; rpc->out = client.out; strbuf_init(&rpc->result, 0); strbuf_addf(&buf, "%s%s", url.buf, svc); rpc->service_url = strbuf_detach(&buf, NULL); strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc); rpc->hdr_content_type = strbuf_detach(&buf, NULL); strbuf_addf(&buf, "Accept: application/x-%s-result", svc); rpc->hdr_accept = strbuf_detach(&buf, NULL); while (!err) { int n = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0); if (!n) break; rpc->pos = 0; rpc->len = n; err |= post_rpc(rpc); } close(client.in); client.in = -1; if (!err) { strbuf_read(&rpc->result, client.out, 0); } else { char buf[4096]; for (;;) if (xread(client.out, buf, sizeof(buf)) <= 0) break; } close(client.out); client.out = -1; err |= finish_command(&client); free(rpc->service_url); free(rpc->hdr_content_type); free(rpc->hdr_accept); free(rpc->buf); strbuf_release(&buf); return err; }
static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out, const struct fetch_pack_args *args, const struct ref *wants, struct oidset *common, int *haves_to_send, int *in_vain) { int ret = 0; struct strbuf req_buf = STRBUF_INIT; if (server_supports_v2("fetch", 1)) packet_buf_write(&req_buf, "command=fetch"); if (server_supports_v2("agent", 0)) packet_buf_write(&req_buf, "agent=%s", git_user_agent_sanitized()); if (args->server_options && args->server_options->nr && server_supports_v2("server-option", 1)) { int i; for (i = 0; i < args->server_options->nr; i++) packet_write_fmt(fd_out, "server-option=%s", args->server_options->items[i].string); } packet_buf_delim(&req_buf); if (args->use_thin_pack) packet_buf_write(&req_buf, "thin-pack"); if (args->no_progress) packet_buf_write(&req_buf, "no-progress"); if (args->include_tag) packet_buf_write(&req_buf, "include-tag"); if (prefer_ofs_delta) packet_buf_write(&req_buf, "ofs-delta"); /* Add shallow-info and deepen request */ if (server_supports_feature("fetch", "shallow", 0)) add_shallow_requests(&req_buf, args); else if (is_repository_shallow(the_repository) || args->deepen) die(_("Server does not support shallow requests")); /* Add filter */ if (server_supports_feature("fetch", "filter", 0) && args->filter_options.choice) { print_verbose(args, _("Server supports filter")); packet_buf_write(&req_buf, "filter %s", args->filter_options.filter_spec); } else if (args->filter_options.choice) { warning("filtering not recognized by server, ignoring"); } /* add wants */ add_wants(args->no_dependents, wants, &req_buf); if (args->no_dependents) { packet_buf_write(&req_buf, "done"); ret = 1; } else { /* Add all of the common commits we've found in previous rounds */ add_common(&req_buf, common); /* Add initial haves */ ret = add_haves(negotiator, &req_buf, haves_to_send, in_vain); } /* Send request */ packet_buf_flush(&req_buf); write_or_die(fd_out, req_buf.buf, req_buf.len); strbuf_release(&req_buf); return ret; }
static void end_headers(struct strbuf *hdr) { strbuf_add(hdr, "\r\n", 2); write_or_die(1, hdr->buf, hdr->len); strbuf_release(hdr); }
static void test_child_private (gchar *argv0) { GError *error = NULL; GMappedFile *map; gchar *buffer; gsize len; gchar *child_argv[4]; GPid child_pid; GMainLoop *loop; gchar pid[100]; #ifdef G_OS_WIN32 g_remove ("STOP"); g_assert (!g_file_test ("STOP", G_FILE_TEST_EXISTS)); #endif write_or_die (filename, "ABC", -1); map = map_or_die (filename, TRUE); #ifndef G_OS_WIN32 signal (SIGUSR1, handle_usr1); #endif g_snprintf (pid, sizeof(pid), "%d", getpid ()); child_argv[0] = argv0; child_argv[1] = "mapchild"; child_argv[2] = pid; child_argv[3] = NULL; if (!g_spawn_async (dir, child_argv, NULL, 0, NULL, NULL, &child_pid, &error)) { g_print ("failed to spawn child: %s\n", error->message); exit (1); } g_message ("test_child_private: child spawned"); #ifndef G_OS_WIN32 loop = g_main_loop_new (NULL, FALSE); g_idle_add (check_stop, loop); g_main_loop_run (loop); stop = FALSE; #else g_usleep (2000000); #endif g_message ("test_child_private: received first child signal"); buffer = (gchar *)g_mapped_file_get_contents (map); buffer[0] = '1'; buffer[1] = '2'; buffer[2] = '3'; g_mapped_file_free (map); #ifndef G_OS_WIN32 kill (child_pid, SIGUSR1); #else g_file_set_contents ("STOP", "Hey there\n", -1, NULL); #endif #ifndef G_OS_WIN32 g_idle_add (check_stop, loop); g_main_loop_run (loop); #else g_usleep (2000000); #endif g_message ("test_child_private: received second child signal"); if (!g_file_get_contents (childname, &buffer, &len, &error)) { gchar *name; name = g_filename_display_name (childname); g_print ("failed to read '%s': %s\n", name, error->message); exit (1); } g_assert (len == 3); g_assert (strcmp (buffer, "ABC") == 0); g_free (buffer); g_message ("test_child_private: ok"); }
static void print_helper_status(struct ref *ref) { struct strbuf buf = STRBUF_INIT; for (; ref; ref = ref->next) { const char *msg = NULL; const char *res; switch(ref->status) { case REF_STATUS_NONE: res = "error"; msg = "no match"; break; case REF_STATUS_OK: res = "ok"; break; case REF_STATUS_UPTODATE: res = "ok"; msg = "up to date"; break; case REF_STATUS_REJECT_NONFASTFORWARD: res = "error"; msg = "non-fast forward"; break; case REF_STATUS_REJECT_FETCH_FIRST: res = "error"; msg = "fetch first"; break; case REF_STATUS_REJECT_NEEDS_FORCE: res = "error"; msg = "needs force"; break; case REF_STATUS_REJECT_STALE: res = "error"; msg = "stale info"; break; case REF_STATUS_REJECT_ALREADY_EXISTS: res = "error"; msg = "already exists"; break; case REF_STATUS_REJECT_NODELETE: case REF_STATUS_REMOTE_REJECT: res = "error"; break; case REF_STATUS_EXPECTING_REPORT: default: continue; } strbuf_reset(&buf); strbuf_addf(&buf, "%s %s", res, ref->name); if (ref->remote_status) msg = ref->remote_status; if (msg) { strbuf_addch(&buf, ' '); quote_two_c_style(&buf, "", msg, 0); } strbuf_addch(&buf, '\n'); write_or_die(1, buf.buf, buf.len); } strbuf_release(&buf); }