/* * Create a newly-allocated `snapshot` of the `packed-refs` file in * its current state and return it. The return value will already have * its reference count incremented. * * A comment line of the form "# pack-refs with: " may contain zero or * more traits. We interpret the traits as follows: * * Neither `peeled` nor `fully-peeled`: * * Probably no references are peeled. But if the file contains a * peeled value for a reference, we will use it. * * `peeled`: * * References under "refs/tags/", if they *can* be peeled, *are* * peeled in this file. References outside of "refs/tags/" are * probably not peeled even if they could have been, but if we find * a peeled value for such a reference we will use it. * * `fully-peeled`: * * All references in the file that can be peeled are peeled. * Inversely (and this is more important), any references in the * file for which no peeled value is recorded is not peelable. This * trait should typically be written alongside "peeled" for * compatibility with older clients, but we do not require it * (i.e., "peeled" is a no-op if "fully-peeled" is set). * * `sorted`: * * The references in this file are known to be sorted by refname. */ static struct snapshot *create_snapshot(struct packed_ref_store *refs) { struct snapshot *snapshot = xcalloc(1, sizeof(*snapshot)); int sorted = 0; snapshot->refs = refs; acquire_snapshot(snapshot); snapshot->peeled = PEELED_NONE; if (!load_contents(snapshot)) return snapshot; /* If the file has a header line, process it: */ if (snapshot->buf < snapshot->eof && *snapshot->buf == '#') { struct strbuf tmp = STRBUF_INIT; char *p; const char *eol; struct string_list traits = STRING_LIST_INIT_NODUP; eol = memchr(snapshot->buf, '\n', snapshot->eof - snapshot->buf); if (!eol) die_unterminated_line(refs->path, snapshot->buf, snapshot->eof - snapshot->buf); strbuf_add(&tmp, snapshot->buf, eol - snapshot->buf); if (!skip_prefix(tmp.buf, "# pack-refs with:", (const char **)&p)) die_invalid_line(refs->path, snapshot->buf, snapshot->eof - snapshot->buf); string_list_split_in_place(&traits, p, ' ', -1); if (unsorted_string_list_has_string(&traits, "fully-peeled")) snapshot->peeled = PEELED_FULLY; else if (unsorted_string_list_has_string(&traits, "peeled")) snapshot->peeled = PEELED_TAGS; sorted = unsorted_string_list_has_string(&traits, "sorted"); /* perhaps other traits later as well */ /* The "+ 1" is for the LF character. */ snapshot->header_len = eol + 1 - snapshot->buf; string_list_clear(&traits, 0); strbuf_release(&tmp); } verify_buffer_safe(snapshot); if (!sorted) { sort_snapshot(snapshot); /* * Reordering the records might have moved a short one * to the end of the buffer, so verify the buffer's * safety again: */ verify_buffer_safe(snapshot); } if (mmap_strategy != MMAP_OK && snapshot->mmapped) { /* * We don't want to leave the file mmapped, so we are * forced to make a copy now: */ size_t size = snapshot->eof - (snapshot->buf + snapshot->header_len); char *buf_copy = xmalloc(size); memcpy(buf_copy, snapshot->buf + snapshot->header_len, size); clear_snapshot_buffer(snapshot); snapshot->buf = buf_copy; snapshot->eof = buf_copy + size; } return snapshot; }
trial<actor> spawn(message const& params) { auto schema_file = ""s; auto output = "-"s; auto r = params.extract_opts({ {"schema,s", "alternate schema file", schema_file}, {"write,w", "path to write events to", output}, {"uds,u", "treat -w as UNIX domain socket to connect to"} }); // Setup a custom schema. schema sch; if (!schema_file.empty()) { auto t = load_contents(schema_file); if (!t) return t.error(); auto s = to<schema>(*t); if (!s) return error{"failed to load schema"}; sch = std::move(*s); } // Facilitate actor shutdown when returning with error. actor snk; auto guard = make_scope_guard([&] { anon_send_exit(snk, exit::error); }); // The "pcap" and "bro" sink manually handle file output. All other // sources are file-based and we setup their input stream here. auto& format = params.get_as<std::string>(0); std::unique_ptr<std::ostream> out; if (!(format == "pcap" || format == "bro")) { if (r.opts.count("uds") > 0) { if (output == "-") return error{"cannot use stdout as UNIX domain socket"}; auto uds = util::unix_domain_socket::connect(output); if (!uds) return error{"failed to connect to UNIX domain socket at ", output}; auto remote_fd = uds.recv_fd(); // Blocks! out = std::make_unique<util::fdostream>(remote_fd); } else if (output == "-") { out = std::make_unique<util::fdostream>(1); // stdout } else { out = std::make_unique<std::ofstream>(output); } } if (format == "pcap") { #ifndef VAST_HAVE_PCAP return error{"not compiled with pcap support"}; #else auto flush = 10000u; r = r.remainder.extract_opts({ {"flush,f", "flush to disk after this many packets", flush} }); if (!r.error.empty()) return error{std::move(r.error)}; snk = caf::spawn<priority_aware>(pcap, sch, output, flush); #endif } else if (format == "bro") { snk = caf::spawn(bro, output); } else if (format == "csv") { snk = caf::spawn(csv, std::move(out)); } else if (format == "ascii") { snk = caf::spawn(ascii, std::move(out)); } else if (format == "json") { r = r.remainder.extract_opts({ {"flatten,f", "flatten records"} }); snk = caf::spawn(sink::json, std::move(out), r.opts.count("flatten") > 0); // FIXME: currently the "vast export" command cannot take sink parameters, // which is why we add a hacky convenience sink called "flat-json". We should // have a command line format akin to "vast export json -f query ...", // which would allow passing both sink and exporter arguments. } else if (format == "flat-json") { snk = caf::spawn(sink::json, std::move(out), true); } else { return error{"invalid export format: ", format}; } guard.disable(); return snk; }