func main()

in tcpdumpw/main.go [607:808]


func main() {
	flag.Parse()

	ctx, cancel := context.WithCancel(context.Background())
	defer func() {
		if r := recover(); r != nil {
			jlog(FATAL, &emptyTcpdumpJob, stringFormatter.Format("panic: {0}", r))
			fmt.Fprintln(os.Stderr, string(debug.Stack()))
		}
	}()

	jid.Store(uuid.Nil)
	xid.Store(uuid.Nil)

	if *compat || strings.EqualFold(*filter, "DISABLED") {
		*filter = ""
	} else {
		*filter = strings.TrimSpace(*filter)
	}

	compatFilters := pcap.NewPcapFilters()
	filters := []pcap.PcapFilterProvider{}

	if *compat || *filter == "" {
		// if complex filter is empty, build it using 'Simple PCAP filters'
		filters = appendFilter(ctx, filters, compatFilters, l3_protos, pcapFilter.NewL3ProtoFilterProvider)
		filters = appendFilter(ctx, filters, compatFilters, l4_protos, pcapFilter.NewL4ProtoFilterProvider)
		filters = appendFilter(ctx, filters, compatFilters, ports, pcapFilter.NewPortsFilterProvider)
		filters = appendFilter(ctx, filters, compatFilters, tcp_flags, pcapFilter.NewTCPFlagsFilterProvider)

		ipFilterProvider := pcapFilter.NewIPFilterProvider(ipv4, ipv6, hosts, compatFilters)
		if _, ok := ipFilterProvider.Get(ctx); ok {
			jlog(INFO, &emptyTcpdumpJob, stringFormatter.Format("using filter: {0}", ipFilterProvider.String()))
			filters = append(filters, ipFilterProvider)
		}

		if len(filters) == 0 && !*compat {
			// if no simple filters are available:
			//   - use a default 'catch-all' filter
			//   		- but only if compat mode is disabled
			*filter = string(pcap.PcapDefaultFilter)
		}
	}

	noProcsInterval := *no_procs_interval
	if noProcsInterval > maxNoProcsInterval {
		noProcsInterval = maxNoProcsInterval
	}
	processFilter := pcapFilter.NewProcessFilterProvider(supervisor, no_procs, uint8(noProcsInterval), *no_procs_debug, compatFilters)
	// initialize `ProcessFilterProvider` for its side effects
	filters = append(filters, processFilter.Initialize(ctx))

	ephemeralPortRange := parseEphemeralPorts(ephemerals)

	pcapVerbosity := parsePcapVerbosity(pcap_verbosity)

	tasks := createTasks(ctx, pcap_iface, timezone, directory, extension,
		filter, filters, compatFilters, snaplen, interval, compat, pcap_debug, tcp_dump,
		json_dump, json_log, ordered, conntrack, gcp_gae, ephemeralPortRange, pcapVerbosity)

	if len(tasks) == 0 {
		jlog(FATAL, &emptyTcpdumpJob, "no PCAP tasks available")
		os.Exit(1)
	}

	pcapMutex := flock.New(pcapLockFile)
	if locked, lockErr := pcapMutex.TryLock(); !locked || lockErr != nil {
		jlog(FATAL, &emptyTcpdumpJob, fmt.Sprintf("failed to acquire PCAP lock | locked: %t | %v", locked, lockErr))
		os.Exit(2)
	}

	jobs = haxmap.New[string, *tcpdumpJob]()

	timeout := time.Duration(*duration) * time.Second
	jlog(INFO, &emptyTcpdumpJob, fmt.Sprintf("parsed timeout: %v", timeout))

	// the file to be created when `tcpdumpw` exists
	exitSignal := fmt.Sprintf("%s/TCPDUMPW_EXITED", *directory)

	// receives status of TCP listener termination: `true` means successful
	tcpStopChannel := make(chan bool, 1)

	// create empty job: used if CRON is not enabled
	job := &tcpdumpJob{
		Jid:   uuid.Nil.String(),
		tasks: tasks,
		debug: *pcap_debug,
	}

	jlog(INFO, job, fmt.Sprintf("acquired PCAP lock: %s", pcapLockFile))

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		signal := <-signals
		jlog(INFO, job, fmt.Sprintf("signaled: %v", signal))
		cancel()
		// unblock TCP listener; next iteration will find `ctx` done
		conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", *hc_port))
		if err == nil {
			conn.Close()
		}
	}()

	// Skip scheduling, execute `tcpdump` immediately
	if !*use_cron {
		id := uuid.New().String()
		ctx = context.WithValue(ctx, pcap.PcapContextID, id)
		logName := fmt.Sprintf("projects/%s/pcaps/%s", os.Getenv("PROJECT_ID"), id)
		ctx = context.WithValue(ctx, pcap.PcapContextLogName, logName)
		ctx = context.WithValue(ctx, pcap.PcapContextDebug, *pcap_debug)
		ctx = context.WithValue(ctx, pcap.PcapContextVerbosity, pcapVerbosity)
		// start the TCP listener for health checks
		go startTCPListener(ctx, hc_port, job, tcpStopChannel)
		start(ctx, &timeout, job)
		waitDone(job, pcapMutex, &exitSignal)
		<-tcpStopChannel
		close(tcpStopChannel)
		return
	}

	// The `timezone` to be used when scheduling `tcpdump` cron jobs
	location, err := time.LoadLocation(*timezone)
	if err != nil {
		*timezone = "UTC"
		location = time.UTC
		jlog(ERROR, &emptyTcpdumpJob, fmt.Sprintf("could not load timezone '%s': %v", *timezone, err))
	}
	jlog(INFO, &emptyTcpdumpJob, fmt.Sprintf("parsed timezone: %v", location))

	// Create a scheduler using the requested timezone.
	// no more than 1 packet capturing job (all its tasks) should ever be executed.
	s, err := gocron.NewScheduler(
		gocron.WithLimitConcurrentJobs(1, gocron.LimitModeReschedule),
		gocron.WithLocation(location),
		gocron.WithGlobalJobOptions(
			gocron.WithTags(
				os.Getenv("PROJECT_ID"),
				os.Getenv("APP_SERVICE"),
				os.Getenv("GCP_REGION"),
				os.Getenv("APP_REVISION"),
				os.Getenv("INSTANCE_ID"),
			),
		),
	)
	if err != nil {
		jlog(ERROR, &emptyTcpdumpJob, fmt.Sprintf("failed to create scheduler: %v", err))
		os.Exit(3)
	}

	// Use the provided `cron` expression ro schedule the packet capturing job
	j, err := s.NewJob(
		gocron.CronJob(fmt.Sprintf("TZ=%s %s", *timezone, *cron_exp), true),
		gocron.NewTask(tcpdump, timeout, *pcap_debug, pcapVerbosity),
		gocron.WithName("tcpdump"),
		gocron.WithSingletonMode(gocron.LimitModeReschedule),
		gocron.WithEventListeners(
			gocron.AfterJobRuns(afterTcpdump),
			gocron.BeforeJobRuns(beforeTcpdump),
		),
	)
	if err != nil {
		jlog(ERROR, &emptyTcpdumpJob, fmt.Sprintf("failed to create scheduled job: %v", err))
		s.Shutdown()
		os.Exit(4)
	}

	jid.Store(j.ID())

	// redefine default `job` with the scheduled one
	job = &tcpdumpJob{
		ctx:   ctx,
		tasks: tasks,
		Jid:   j.ID().String(),
		Name:  j.Name(),
		Tags:  j.Tags(),
		j:     &j,
	}
	jobs.Set(job.Jid, job)
	jlog(INFO, job, "scheduled job")

	// Start the packet capturing scheduler
	s.Start()

	nextRun, _ := j.NextRun()
	jlog(INFO, job, fmt.Sprintf("next execution: %v", nextRun))

	// start the TCP listener for health checks
	go startTCPListener(ctx, hc_port, job, tcpStopChannel)

	// Block main goroutine until a signal is received
	<-ctx.Done()

	s.StopJobs()
	s.RemoveJob(j.ID())
	s.Shutdown()
	jlog(INFO, job, "scheduler terminated")

	waitDone(job, pcapMutex, &exitSignal)
	<-tcpStopChannel
	close(tcpStopChannel)
}