]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - term-utils/script-playutils.c
14 #include "closestream.h"
17 #include "script-playutils.h"
19 UL_DEBUG_DEFINE_MASK(scriptreplay
);
20 UL_DEBUG_DEFINE_MASKNAMES(scriptreplay
) = UL_DEBUG_EMPTY_MASKNAMES
;
22 #define DBG(m, x) __UL_DBG(scriptreplay, SCRIPTREPLAY_DEBUG_, m, x)
23 #define ON_DBG(m, x) __UL_DBG_CALL(scriptreplay, SCRIPTREPLAY_DEBUG_, m, x)
26 * The script replay is driven by timing file where each entry describes one
27 * step in the replay. The timing step may refer input or output (or
28 * signal, extra informations, etc.)
30 * The step data are stored in log files, the right log file for the step is
31 * selected from replay_setup.
34 REPLAY_TIMING_SIMPLE
, /* timing info in classic "<delta> <offset>" format */
35 REPLAY_TIMING_MULTI
/* multiple streams in format "<type> <delta> <offset|etc> */
39 const char *streams
; /* 'I'nput, 'O'utput or both */
43 unsigned int noseek
; /* do not seek in this log */
47 char type
; /* 'I'nput, 'O'utput, ... */
50 char *name
; /* signals / heders */
54 struct replay_log
*data
;
58 struct replay_log
*logs
;
61 struct replay_step step
; /* current step */
64 const char *timing_filename
;
68 struct timeval delay_max
;
69 struct timeval delay_min
;
72 char default_type
; /* type for REPLAY_TIMING_SIMPLE */
76 void replay_init_debug(void)
78 __UL_INIT_DEBUG_FROM_ENV(scriptreplay
, SCRIPTREPLAY_DEBUG_
, 0, SCRIPTREPLAY_DEBUG
);
81 static int ignore_line(FILE *f
)
85 while((c
= fgetc(f
)) != EOF
&& c
!= '\n');
89 DBG(LOG
, ul_debug(" ignore line"));
93 /* incretemt @a by @b */
94 static inline void timerinc(struct timeval
*a
, struct timeval
*b
)
99 a
->tv_sec
= res
.tv_sec
;
100 a
->tv_usec
= res
.tv_usec
;
103 struct replay_setup
*replay_new_setup(void)
105 return xcalloc(1, sizeof(struct replay_setup
));
108 /* if timing file does not contains types of entries (old format) than use this
109 * type as the default */
110 int replay_set_default_type(struct replay_setup
*stp
, char type
)
113 stp
->default_type
= type
;
118 int replay_set_crmode(struct replay_setup
*stp
, int mode
)
126 int replay_set_delay_min(struct replay_setup
*stp
, const struct timeval
*tv
)
128 stp
->delay_min
.tv_sec
= tv
->tv_sec
;
129 stp
->delay_min
.tv_usec
= tv
->tv_usec
;
133 int replay_set_delay_max(struct replay_setup
*stp
, const struct timeval
*tv
)
135 stp
->delay_max
.tv_sec
= tv
->tv_sec
;
136 stp
->delay_max
.tv_usec
= tv
->tv_usec
;
140 int replay_set_delay_div(struct replay_setup
*stp
, const double divi
)
142 stp
->delay_div
= divi
;
146 static struct replay_log
*replay_new_log(struct replay_setup
*stp
,
148 const char *filename
,
151 struct replay_log
*log
;
157 stp
->logs
= xrealloc(stp
->logs
, (stp
->nlogs
+ 1) * sizeof(*log
));
158 log
= &stp
->logs
[stp
->nlogs
];
161 log
->filename
= filename
;
162 log
->streams
= streams
;
168 int replay_set_timing_file(struct replay_setup
*stp
, const char *filename
)
175 stp
->timing_filename
= filename
;
176 stp
->timing_line
= 0;
178 stp
->timing_fp
= fopen(filename
, "r");
182 /* detect timing file format */
183 c
= fgetc(stp
->timing_fp
);
185 if (isdigit((unsigned int) c
))
186 stp
->timing_format
= REPLAY_TIMING_SIMPLE
;
188 stp
->timing_format
= REPLAY_TIMING_MULTI
;
189 ungetc(c
, stp
->timing_fp
);
190 } else if (ferror(stp
->timing_fp
))
194 if (rc
&& stp
->timing_fp
) {
195 fclose(stp
->timing_fp
);
196 stp
->timing_fp
= NULL
;
199 /* create quasi-log for signals, headers, etc. */
200 if (rc
== 0 && stp
->timing_format
== REPLAY_TIMING_MULTI
) {
201 struct replay_log
*log
= replay_new_log(stp
, "SH",
202 filename
, stp
->timing_fp
);
207 DBG(LOG
, ul_debug("accociate file '%s' for streams 'SH'", filename
));
211 DBG(TIMING
, ul_debug("timing file set to '%s' [rc=%d]", filename
, rc
));
215 const char *replay_get_timing_file(struct replay_setup
*setup
)
218 return setup
->timing_filename
;
221 int replay_get_timing_line(struct replay_setup
*setup
)
224 return setup
->timing_line
;
227 int replay_associate_log(struct replay_setup
*stp
,
228 const char *streams
, const char *filename
)
237 /* open the file and skip the first line */
238 f
= fopen(filename
, "r");
239 rc
= f
== NULL
? -errno
: ignore_line(f
);
242 replay_new_log(stp
, streams
, filename
, f
);
244 DBG(LOG
, ul_debug("accociate log file '%s', streams '%s' [rc=%d]", filename
, streams
, rc
));
248 static int is_wanted_stream(char type
, const char *streams
)
252 if (strchr(streams
, type
))
257 static void replay_reset_step(struct replay_step
*step
)
264 timerclear(&step
->delay
);
267 struct timeval
*replay_step_get_delay(struct replay_step
*step
)
273 /* current data log file */
274 const char *replay_step_get_filename(struct replay_step
*step
)
277 return step
->data
->filename
;
280 int replay_step_is_empty(struct replay_step
*step
)
283 return step
->size
== 0 && step
->type
== 0;
287 static int read_multistream_step(struct replay_step
*step
, FILE *f
, char type
)
294 case 'O': /* output */
295 case 'I': /* input */
296 rc
= fscanf(f
, "%ld.%06ld %zu%c\n",
298 &step
->delay
.tv_usec
,
300 if (rc
!= 4 || nl
!= '\n')
306 case 'S': /* signal */
307 case 'H': /* header */
311 rc
= fscanf(f
, "%ld.%06ld ",
313 &step
->delay
.tv_usec
);
318 rc
= fscanf(f
, "%s", buf
); /* name */
321 step
->name
= strrealloc(step
->name
, buf
);
325 if (!fgets(buf
, sizeof(buf
), f
)) { /* value */
331 step
->value
= strrealloc(step
->value
, buf
);
342 DBG(TIMING
, ul_debug(" read step delay & size [rc=%d]", rc
));
346 static struct replay_log
*replay_get_stream_log(struct replay_setup
*stp
, char stream
)
350 for (i
= 0; i
< stp
->nlogs
; i
++) {
351 struct replay_log
*log
= &stp
->logs
[i
];
353 if (is_wanted_stream(stream
, log
->streams
))
359 static int replay_seek_log(struct replay_log
*log
, size_t move
)
364 DBG(LOG
, ul_debug(" %s: seek ++ %zu", log
->filename
, move
));
365 return fseek(log
->fp
, move
, SEEK_CUR
) == (off_t
) -1 ? -errno
: 0;
368 /* returns next step with pointer to the right log file for specified streams (e.g.
369 * "IOS" for in/out/signals) or all streams if stream is NULL.
371 * returns: 0 = success, <0 = error, 1 = done (EOF)
373 int replay_get_next_step(struct replay_setup
*stp
, char *streams
, struct replay_step
**xstep
)
375 struct replay_step
*step
;
377 struct timeval ignored_delay
;
380 assert(stp
->timing_fp
);
386 timerclear(&ignored_delay
);
389 struct replay_log
*log
= NULL
;
392 if (feof(stp
->timing_fp
))
395 DBG(TIMING
, ul_debug("reading next step"));
397 replay_reset_step(step
);
400 switch (stp
->timing_format
) {
401 case REPLAY_TIMING_SIMPLE
:
402 /* old format is the same as new format, but without <type> prefix */
403 rc
= read_multistream_step(step
, stp
->timing_fp
, stp
->default_type
);
405 step
->type
= stp
->default_type
;
407 case REPLAY_TIMING_MULTI
:
408 rc
= fscanf(stp
->timing_fp
, "%c ", &step
->type
);
412 rc
= read_multistream_step(step
,
419 if (rc
< 0 && feof(stp
->timing_fp
))
421 break; /* error or EOF */
424 DBG(TIMING
, ul_debug(" step entry is '%c'", step
->type
));
426 log
= replay_get_stream_log(stp
, step
->type
);
428 if (is_wanted_stream(step
->type
, streams
)) {
431 DBG(LOG
, ul_debug(" use %s as data source", log
->filename
));
434 /* The step entry is unwanted, but we keep the right
435 * position in the log file although the data are ignored.
437 replay_seek_log(log
, step
->size
);
439 DBG(TIMING
, ul_debug(" not found log for '%c' stream", step
->type
));
441 DBG(TIMING
, ul_debug(" ignore step '%c' [delay=%ld.%06ld]",
444 step
->delay
.tv_usec
));
446 timerinc(&ignored_delay
, &step
->delay
);
450 if (timerisset(&ignored_delay
))
451 timerinc(&step
->delay
, &ignored_delay
);
453 DBG(TIMING
, ul_debug("reading next step done [rc=%d delay=%ld.%06ld (ignored=%ld.%06ld) size=%zu]",
455 step
->delay
.tv_sec
, step
->delay
.tv_usec
,
456 ignored_delay
.tv_sec
, ignored_delay
.tv_usec
,
459 /* normalize delay */
460 if (stp
->delay_div
) {
461 DBG(TIMING
, ul_debug(" normalize delay: divide"));
462 step
->delay
.tv_sec
/= stp
->delay_div
;
463 step
->delay
.tv_usec
/= stp
->delay_div
;
465 if (timerisset(&stp
->delay_max
) &&
466 timercmp(&step
->delay
, &stp
->delay_max
, >)) {
467 DBG(TIMING
, ul_debug(" normalize delay: align to max"));
468 step
->delay
.tv_sec
= stp
->delay_max
.tv_sec
;
469 step
->delay
.tv_usec
= stp
->delay_max
.tv_usec
;
471 if (timerisset(&stp
->delay_min
) &&
472 timercmp(&step
->delay
, &stp
->delay_min
, <)) {
473 DBG(TIMING
, ul_debug(" normalize delay: align to min"));
474 timerclear(&step
->delay
);
480 /* return: 0 = success, <0 = error, 1 = done (EOF) */
481 int replay_emit_step_data(struct replay_setup
*stp
, struct replay_step
*step
, int fd
)
484 int rc
= 0, cr2nl
= 0;
489 switch (step
->type
) {
493 dprintf(fd
, "%s %s\n", step
->name
, step
->value
);
494 DBG(LOG
, ul_debug("log signal emited"));
499 dprintf(fd
, "%10s: %s\n", step
->name
, step
->value
);
500 DBG(LOG
, ul_debug("log header emited"));
503 break; /* continue with real data */
508 assert(step
->data
->fp
);
510 switch (stp
->crmode
) {
511 case REPLAY_CRMODE_AUTO
:
512 if (step
->type
== 'I')
515 case REPLAY_CRMODE_NEVER
:
518 case REPLAY_CRMODE_ALWAYS
:
523 for (ct
= step
->size
; ct
> 0; ) {
526 cc
= ct
> sizeof(buf
) ? sizeof(buf
): ct
;
527 len
= fread(buf
, 1, cc
, step
->data
->fp
);
530 DBG(LOG
, ul_debug("log data emit: failed to read log %m"));
537 for (i
= 0; i
< len
; i
++) {
544 cc
= write(fd
, buf
, len
);
547 DBG(LOG
, ul_debug("log data emit: failed write data %m"));
552 if (ct
&& ferror(step
->data
->fp
))
554 if (ct
&& feof(step
->data
->fp
))
557 DBG(LOG
, ul_debug("log data emited [rc=%d size=%zu]", rc
, step
->size
));