/* * Functions internal to the PV library. Include "config.h" first. * * Copyright 2002-2008, 2010, 2012-2015, 2017, 2021, 2023-2024 Andrew Wood * * License GPLv3+: GNU GPL version 3 or later; see `docs/COPYING'. */ #ifndef _PV_INTERNAL_H #define _PV_INTERNAL_H 1 #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * Types of display component that make up an output string. */ typedef enum { PV_COMPONENT_STRING, /* fixed string */ PV_COMPONENT_PROGRESS, /* progress bar, with percentage if known */ PV_COMPONENT_BYTES, /* number of bytes transferred */ PV_COMPONENT_TIMER, /* elapsed time */ PV_COMPONENT_RATE, /* current transfer rate */ PV_COMPONENT_AVERAGERATE, /* average transfer rate */ PV_COMPONENT_ETA, /* estimated time remaining until completion */ PV_COMPONENT_FINETA, /* estimated time of completion */ PV_COMPONENT_NAME, /* name prefix */ PV_COMPONENT_BUFPERCENT, /* percentage of buffer used */ PV_COMPONENT_OUTPUTBUF, /* recent bytes in output buffer */ PV_COMPONENT__MAX } pv_display_component; #define PV_SIZEOF_COMPONENT_STR 1024 /* size of buffer for each component */ #define RATE_GRANULARITY 100000000 /* nsec between -L rate chunks */ #define RATE_BURST_WINDOW 5 /* rate burst window (multiples of rate) */ #define REMOTE_INTERVAL 100000000 /* nsec between checks for -R */ #define BUFFER_SIZE (size_t) 409600 /* default transfer buffer size */ #define BUFFER_SIZE_MAX (size_t) 524288 /* max auto transfer buffer size */ #define MAX_READ_AT_ONCE (size_t) 524288 /* max to read() in one go */ #define MAX_WRITE_AT_ONCE (size_t) 524288 /* max to write() in one go */ #define TRANSFER_READ_TIMEOUT 0.09L /* seconds to time reads out at */ #define TRANSFER_WRITE_TIMEOUT 0.9L /* seconds to time writes out at */ #define MAXIMISE_BUFFER_FILL 1 #define PV_SIZEOF_DEFAULT_FORMAT 512 #define PV_SIZEOF_CWD 4096 #define PV_SIZEOF_LASTOUTPUT_BUFFER 256 #define PV_FORMAT_ARRAY_MAX 100 #define PV_SIZEOF_CRS_LOCK_FILE 1024 #define PV_SIZEOF_FILE_FDINFO 4096 #define PV_SIZEOF_FILE_FD 4096 #define PV_SIZEOF_FILE_FDPATH 4096 #define PV_SIZEOF_DISPLAY_NAME 512 /* * Structure for data shared between multiple "pv -c" instances. */ struct pvcursorstate_s { int y_topmost; /* terminal row of topmost "pv" instance */ bool tty_tostop_added; /* whether any instance had to set TOSTOP on the terminal */ }; /* * Structure for holding PV internal state. Opaque outside the PV library. * * In general, members are ordered by size, to minimise padding. */ struct pvstate_s { /****************** * Program status * ******************/ struct { /*@only@*/ char *program_name; /* program name for error reporting */ char cwd[PV_SIZEOF_CWD]; /* current working directory for relative path */ int current_input_file; /* index of current file being read */ int exit_status; /* exit status to give (0=OK) */ } status; /*************** * Input files * ***************/ struct { /*@only@*/ /*@null@*/ char **filename; /* input filenames */ unsigned int file_count; /* number of input files */ } files; /******************* * Program control * *******************/ struct { char default_format[PV_SIZEOF_DEFAULT_FORMAT]; /* default format string */ double interval; /* interval between updates */ double delay_start; /* delay before first display */ /*@only@*/ /*@null@*/ char *name; /* display name */ /*@only@*/ /*@null@*/ char *format_string; /* output format string */ /*@null@*/ char *output_name; /* name of the output, for diagnostics */ off_t error_skip_block; /* skip block size, 0 for adaptive */ off_t rate_limit; /* rate limit, in bytes per second */ size_t target_buffer_size; /* buffer size (0=default) */ off_t size; /* total size of data */ pid_t watch_pid; /* process to watch fds of */ unsigned int skip_errors; /* skip read errors counter */ int watch_fd; /* fd to watch */ int output_fd; /* fd to write output to */ unsigned int average_rate_window; /* time window in seconds for average rate calculations */ unsigned int width; /* screen width */ unsigned int height; /* screen height */ bool force; /* display even if not on terminal */ bool cursor; /* use cursor positioning */ bool numeric; /* numeric output only */ bool wait; /* wait for data before display */ bool linemode; /* count lines instead of bytes */ bool bits; /* report bits instead of bytes */ bool decimal_units; /* use decimal prefixes */ bool null_terminated_lines; /* lines are null-terminated */ bool no_display; /* do nothing other than pipe data */ bool stop_at_size; /* set if we stop at "size" bytes */ bool sync_after_write; /* set if we sync after every write */ bool direct_io; /* set if O_DIRECT is to be used */ bool direct_io_changed; /* set when direct_io is changed */ bool no_splice; /* never use splice() */ bool discard_input; /* write nothing to stdout */ bool width_set_manually; /* width was set manually, not detected */ bool height_set_manually; /* height was set manually, not detected */ } control; /******************* * Signal handling * *******************/ struct { /* old signal handlers to restore in pv_sig_fini(). */ struct sigaction old_sigpipe; struct sigaction old_sigttou; struct sigaction old_sigtstp; struct sigaction old_sigcont; struct sigaction old_sigwinch; struct sigaction old_sigint; struct sigaction old_sighup; struct sigaction old_sigterm; #ifdef SA_SIGINFO struct sigaction old_sigusr2; #endif struct sigaction old_sigalrm; struct timespec tstp_time; /* see pv_sig_tstp() / __cont() */ struct timespec toffset; /* total time spent stopped */ #ifdef SA_SIGINFO volatile sig_atomic_t rxusr2; /* whether SIGUSR2 was received */ volatile pid_t sender; /* PID of sending process for SIGUSR2 */ #endif int old_stderr; /* see pv_sig_ttou() */ bool pv_tty_tostop_added; /* whether we had to set TOSTOP on the terminal */ } signal; /******************* * Transient flags * *******************/ struct { volatile sig_atomic_t reparse_display; /* whether to re-check format string */ volatile sig_atomic_t terminal_resized; /* whether we need to get term size again */ volatile sig_atomic_t trigger_exit; /* whether we need to abort right now */ } flag; /***************** * Display state * *****************/ struct { struct { /* format string broken into display components */ size_t str_start; /* for strings: start offset */ size_t str_length; /* for strings: length */ pv_display_component type; /* type of display component */ } format[PV_FORMAT_ARRAY_MAX]; struct { /* display components */ char content[PV_SIZEOF_COMPONENT_STR]; /* string to display */ size_t length; /* number of bytes in string */ bool required; /* true if included in format */ } component[PV_COMPONENT__MAX]; char lastoutput_buffer[PV_SIZEOF_LASTOUTPUT_BUFFER]; long double prev_elapsed_sec; /* elapsed sec at which rate last calculated */ long double prev_rate; /* last calculated instantaneous transfer rate */ long double prev_trans; /* bytes transferred since last rate calculation */ long double current_avg_rate; /* current average rate over last history intervals */ /*@only@*/ /*@null@*/ char *display_buffer; /* buffer for display string */ size_t display_buffer_size; /* size allocated to display buffer */ size_t display_string_len; /* length of string in display buffer */ off_t initial_offset; /* offset when first opened (when watching fds) */ size_t lastoutput_length; /* number of last-output bytes to show */ /* Keep track of progress over last intervals to compute current average rate. */ /*@null@*/ struct { /* state at previous intervals (circular buffer) */ long double elapsed_sec; /* time since start of transfer */ off_t total_bytes; /* amount transferred by that time */ } *history; size_t history_len; /* total size of history array */ size_t history_first; /* index of oldest entry */ size_t history_last; /* index of newest entry */ size_t format_segment_count; /* number of format string segments */ int history_interval; /* seconds between each history entry */ unsigned int prev_screen_width; /* screen width last time we were called */ int percentage; /* transfer percentage completion */ bool display_visible; /* set once anything written to terminal */ } display; /******************** * Cursor/IPC state * ********************/ struct { char lock_file[PV_SIZEOF_CRS_LOCK_FILE]; #ifdef HAVE_IPC /*@keep@*/ /*@null@*/ struct pvcursorstate_s *shared; /* data shared between instances */ int shmid; /* ID of our shared memory segment */ int pvcount; /* number of `pv' processes in total */ int pvmax; /* highest number of `pv's seen */ int y_lastread; /* last value of _y_top seen */ int y_offset; /* our Y offset from this top position */ int needreinit; /* counter if we need to reinit cursor pos */ #endif /* HAVE_IPC */ int lock_fd; /* fd of lockfile, -1 if none open */ int y_start; /* our initial Y coordinate */ #ifdef HAVE_IPC bool noipc; /* set if we can't use IPC */ #endif /* HAVE_IPC */ } cursor; /******************* * Transfer state * *******************/ /* * The transfer buffer is used for moving data from the input files * to the output when splice() is not available. * * If buffer_size is smaller than pv__target_bufsize, then * pv_transfer() will try to reallocate transfer_buffer to make * buffer_size equal to pv__target_bufsize. * * Data from the input files is read into the buffer; read_position * is the offset in the buffer that we've read data up to. * * Data is written to the output from the buffer, and write_position * is the offset in the buffer that we've written data up to. It * will always be less than or equal to read_position. */ struct { /*@only@*/ /*@null@*/ char *transfer_buffer; /* data transfer buffer */ size_t buffer_size; /* size of buffer */ size_t read_position; /* amount of data in buffer */ size_t write_position; /* buffered data written */ ssize_t to_write; /* max to write this time around */ ssize_t written; /* bytes sent to stdout this time */ /* * While reading from a file descriptor we keep track of how * many times in a row we've seen errors * (read_errors_in_a_row), and whether or not we have put a * warning on stderr about read errors on this fd * (read_error_warning_shown). * * Whenever the active file descriptor changes from * last_read_skip_fd, we reset read_errors_in_a_row to 0 and * read_error_warning_shown to false for the new file * descriptor and set last_read_skip_fd to the new fd * number. * * This way, we're treating each input file separately. */ off_t read_errors_in_a_row; int last_read_skip_fd; /* read_error_warning_shown is defined below. */ #ifdef HAVE_SPLICE /* * These variables are used to keep track of whether * splice() was used; splice_failed_fd is the file * descriptor that splice() last failed on, so that we don't * keep trying to use it on an fd that doesn't support it, * and splice_used is set to true if splice() was used this * time within pv_transfer(). */ int splice_failed_fd; bool splice_used; #endif bool read_error_warning_shown; } transfer; }; struct pvwatchfd_s { #ifdef __APPLE__ #else char file_fdinfo[PV_SIZEOF_FILE_FDINFO]; /* path to /proc fdinfo file */ char file_fd[PV_SIZEOF_FILE_FD]; /* path to /proc fd symlink */ #endif char file_fdpath[PV_SIZEOF_FILE_FDPATH]; /* path to file that was opened */ char display_name[PV_SIZEOF_DISPLAY_NAME]; /* name to show on progress bar */ struct stat sb_fd; /* stat of fd symlink */ struct stat sb_fd_link; /* lstat of fd symlink */ off_t size; /* size of whole file, 0 if unknown */ off_t position; /* position last seen at */ struct timespec start_time; /* time we started watching the fd */ /*@null@*/ pvstate_t state; /* state object for flags and display */ pid_t watch_pid; /* PID to watch */ int watch_fd; /* fd to watch, -1 = not displayed */ }; typedef struct pvwatchfd_s *pvwatchfd_t; void pv_error(pvstate_t, char *, ...); int pv_main_loop(pvstate_t); void pv_display(pvstate_t, long double, off_t, off_t); ssize_t pv_transfer(pvstate_t, int, bool *, bool *, off_t, long *); int pv_next_file(pvstate_t, unsigned int, int); /*@out@*/ const char *pv_current_file_name(pvstate_t); void pv_write_retry(int, const char *, size_t); void pv_crs_fini(pvstate_t); void pv_crs_init(pvstate_t); void pv_crs_update(pvstate_t, const char *); #ifdef HAVE_IPC void pv_crs_needreinit(pvstate_t); #endif void pv_sig_allowpause(void); void pv_sig_checkbg(void); void pv_sig_nopause(void); void pv_remote_init(pvstate_t); void pv_remote_check(pvstate_t); void pv_remote_fini(pvstate_t); int pv_remote_set(pvstate_t); int pv_watchfd_info(pvstate_t, pvwatchfd_t, bool); bool pv_watchfd_changed(pvwatchfd_t); off_t pv_watchfd_position(pvwatchfd_t); int pv_watchpid_scanfds(pvstate_t, pid_t, int *, pvwatchfd_t *, int *); void pv_watchpid_setname(pvstate_t, pvwatchfd_t); #ifdef __cplusplus } #endif #endif /* _PV_INTERNAL_H */ /* EOF */