Coverage Report

Created: 2026-08-31 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/cmd-queue.c
Line
Count
Source
1
/* $OpenBSD: cmd-queue.c,v 1.123 2026/08/24 20:34:26 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <ctype.h>
22
#include <pwd.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <time.h>
26
#include <unistd.h>
27
28
#include "tmux.h"
29
30
/* Command queue flags. */
31
0
#define CMDQ_FIRED 0x1
32
0
#define CMDQ_WAITING 0x2
33
34
/* Command queue item type. */
35
enum cmdq_type {
36
  CMDQ_COMMAND,
37
  CMDQ_CALLBACK,
38
};
39
40
/* Command queue item. */
41
struct cmdq_item {
42
  char      *name;
43
  struct cmdq_list  *queue;
44
  struct cmdq_item  *next;
45
46
  struct client   *client;
47
  struct client   *target_client;
48
49
  enum cmdq_type     type;
50
  u_int      group;
51
52
  u_int      number;
53
  time_t       time;
54
55
  int      flags;
56
57
  struct cmdq_state *state;
58
  struct cmd_find_state  source;
59
  struct cmd_find_state  target;
60
61
  struct cmd_list   *cmdlist;
62
  struct cmd    *cmd;
63
64
  cmdq_cb      cb;
65
  void      *data;
66
67
  TAILQ_ENTRY(cmdq_item)   entry;
68
};
69
TAILQ_HEAD(cmdq_item_list, cmdq_item);
70
71
/*
72
 * Command queue state. This is the context for commands on the command queue.
73
 * It holds information about how the commands were fired (the key and flags),
74
 * any additional formats for the commands, and the current default target.
75
 * Multiple commands can share the same state and a command may update the
76
 * default target.
77
 */
78
struct cmdq_state {
79
  int      references;
80
  int      flags;
81
82
  struct format_tree  *formats;
83
84
  struct key_event   event;
85
  struct cmd_find_state  current;
86
};
87
88
/* Command queue. */
89
struct cmdq_list {
90
  struct cmdq_item  *item;
91
  struct cmdq_item_list  list;
92
};
93
94
/* Get command queue name. */
95
static const char *
96
cmdq_name(struct client *c)
97
12.4k
{
98
12.4k
  static char s[256];
99
100
12.4k
  if (c == NULL)
101
12.4k
    return ("<global>");
102
0
  if (c->name != NULL)
103
0
    xsnprintf(s, sizeof s, "<%s>", c->name);
104
0
  else
105
0
    xsnprintf(s, sizeof s, "<%p>", c);
106
0
  return (s);
107
12.4k
}
108
109
/* Get command queue from client. */
110
static struct cmdq_list *
111
cmdq_get(struct client *c)
112
12.4k
{
113
12.4k
  static struct cmdq_list *global_queue;
114
115
12.4k
  if (c == NULL) {
116
12.4k
    if (global_queue == NULL)
117
1
      global_queue = cmdq_new();
118
12.4k
    return (global_queue);
119
12.4k
  }
120
0
  return (c->queue);
121
12.4k
}
122
123
/* Create a queue. */
124
struct cmdq_list *
125
cmdq_new(void)
126
1
{
127
1
  struct cmdq_list  *queue;
128
129
1
  queue = xcalloc(1, sizeof *queue);
130
1
  TAILQ_INIT (&queue->list);
131
1
  return (queue);
132
1
}
133
134
/* Free a queue. */
135
void
136
cmdq_free(struct cmdq_list *queue)
137
0
{
138
0
  if (!TAILQ_EMPTY(&queue->list))
139
0
    fatalx("queue not empty");
140
0
  free(queue);
141
0
}
142
143
/* Get item name. */
144
const char *
145
cmdq_get_name(struct cmdq_item *item)
146
0
{
147
0
  return (item->name);
148
0
}
149
150
/* Get item command. */
151
struct cmd *
152
cmdq_get_cmd(struct cmdq_item *item)
153
0
{
154
0
  return (item->cmd);
155
0
}
156
157
/* Get item client. */
158
struct client *
159
cmdq_get_client(struct cmdq_item *item)
160
0
{
161
0
  return (item->client);
162
0
}
163
164
/* Get item target client. */
165
struct client *
166
cmdq_get_target_client(struct cmdq_item *item)
167
0
{
168
0
  return (item->target_client);
169
0
}
170
171
/* Get item state. */
172
struct cmdq_state *
173
cmdq_get_state(struct cmdq_item *item)
174
0
{
175
0
  return (item->state);
176
0
}
177
178
/* Get item target. */
179
struct cmd_find_state *
180
cmdq_get_target(struct cmdq_item *item)
181
0
{
182
0
  return (&item->target);
183
0
}
184
185
/* Get item source. */
186
struct cmd_find_state *
187
cmdq_get_source(struct cmdq_item *item)
188
0
{
189
0
  return (&item->source);
190
0
}
191
192
/* Get state event. */
193
struct key_event *
194
cmdq_get_event(struct cmdq_item *item)
195
0
{
196
0
  return (&item->state->event);
197
0
}
198
199
/* Get state current target. */
200
struct cmd_find_state *
201
cmdq_get_current(struct cmdq_item *item)
202
0
{
203
0
  return (&item->state->current);
204
0
}
205
206
/* Get state flags. */
207
int
208
cmdq_get_flags(struct cmdq_item *item)
209
0
{
210
0
  return (item->state->flags);
211
0
}
212
213
/* Create a new state. */
214
struct cmdq_state *
215
cmdq_new_state(struct cmd_find_state *current, struct key_event *event,
216
    int flags)
217
0
{
218
0
  struct cmdq_state *state;
219
220
0
  state = xcalloc(1, sizeof *state);
221
0
  state->references = 1;
222
0
  state->flags = flags;
223
224
0
  if (event != NULL)
225
0
    memcpy(&state->event, event, sizeof state->event);
226
0
  else
227
0
    state->event.key = KEYC_NONE;
228
0
  if (current != NULL && cmd_find_valid_state(current))
229
0
    cmd_find_copy_state(&state->current, current);
230
0
  else
231
0
    cmd_find_clear_state(&state->current, 0);
232
233
0
  return (state);
234
0
}
235
236
/* Add a reference to a state. */
237
struct cmdq_state *
238
cmdq_link_state(struct cmdq_state *state)
239
0
{
240
0
  state->references++;
241
0
  return (state);
242
0
}
243
244
/* Make a copy of a state. */
245
struct cmdq_state *
246
cmdq_copy_state(struct cmdq_state *state, struct cmd_find_state *current)
247
0
{
248
0
  if (current != NULL)
249
0
    return (cmdq_new_state(current, &state->event, state->flags));
250
0
  return (cmdq_new_state(&state->current, &state->event, state->flags));
251
0
}
252
253
/* Free a state. */
254
void
255
cmdq_free_state(struct cmdq_state *state)
256
0
{
257
0
  if (--state->references != 0)
258
0
    return;
259
260
0
  if (state->formats != NULL)
261
0
    format_free(state->formats);
262
0
  free(state);
263
0
}
264
265
/* Add a format to command queue. */
266
void
267
cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
268
0
{
269
0
  va_list  ap;
270
0
  char  *value;
271
272
0
  va_start(ap, fmt);
273
0
  xvasprintf(&value, fmt, ap);
274
0
  va_end(ap);
275
276
0
  if (state->formats == NULL)
277
0
    state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
278
0
  format_add(state->formats, key, "%s", value);
279
280
0
  free(value);
281
0
}
282
283
/* Add formats to command queue. */
284
void
285
cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft)
286
0
{
287
0
  if (state->formats == NULL)
288
0
    state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
289
0
  format_merge(state->formats, ft);
290
0
}
291
292
/* Merge formats from item. */
293
void
294
cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
295
0
{
296
0
  const struct cmd_entry  *entry;
297
298
0
  if (item->cmd != NULL) {
299
0
    entry = cmd_get_entry(item->cmd);
300
0
    format_add(ft, "command", "%s", entry->name);
301
0
  }
302
0
  if (item->state->formats != NULL)
303
0
    format_merge(ft, item->state->formats);
304
0
}
305
306
/* Append an item. */
307
struct cmdq_item *
308
cmdq_append(struct client *c, struct cmdq_item *item)
309
0
{
310
0
  struct cmdq_list  *queue = cmdq_get(c);
311
0
  struct cmdq_item  *next;
312
313
0
  do {
314
0
    next = item->next;
315
0
    item->next = NULL;
316
317
0
    if (c != NULL)
318
0
      c->references++;
319
0
    item->client = c;
320
321
0
    item->queue = queue;
322
0
    TAILQ_INSERT_TAIL(&queue->list, item, entry);
323
0
    log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
324
325
0
    item = next;
326
0
  } while (item != NULL);
327
0
  return (TAILQ_LAST(&queue->list, cmdq_item_list));
328
0
}
329
330
/* Insert an item. */
331
struct cmdq_item *
332
cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
333
0
{
334
0
  struct client   *c = after->client;
335
0
  struct cmdq_list  *queue = after->queue;
336
0
  struct cmdq_item  *next;
337
338
0
  do {
339
0
    next = item->next;
340
0
    item->next = after->next;
341
0
    after->next = item;
342
343
0
    if (c != NULL)
344
0
      c->references++;
345
0
    item->client = c;
346
347
0
    item->queue = queue;
348
0
    TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
349
0
    log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
350
0
        item->name, after->name);
351
352
0
    after = item;
353
0
    item = next;
354
0
  } while (item != NULL);
355
0
  return (after);
356
0
}
357
358
/* Insert a hook. */
359
void
360
cmdq_insert_hook(__unused struct session *s, struct cmdq_item *item,
361
    struct cmd_find_state *current, const char *fmt, ...)
362
0
{
363
0
  struct cmd      *cmd = item->cmd;
364
0
  struct args     *args = cmd_get_args(cmd);
365
0
  struct args_entry   *ae;
366
0
  struct args_value   *av;
367
0
  struct event_payload    *ep;
368
0
  va_list        ap;
369
0
  char        *name, tmp[32], flag, *arguments;
370
0
  u_int        i;
371
0
  const char      *value;
372
373
0
  if (item->state->flags & CMDQ_STATE_NOHOOKS)
374
0
    return;
375
376
0
  va_start(ap, fmt);
377
0
  xvasprintf(&name, fmt, ap);
378
0
  va_end(ap);
379
380
0
  ep = event_payload_create();
381
0
  if (current != NULL)
382
0
    event_payload_set_target(ep, current);
383
0
  event_payload_set_pointer(ep, "_cmdq_item", item, NULL, NULL);
384
385
0
  arguments = args_print(args);
386
0
  event_payload_set_string(ep, "arguments", "%s", arguments);
387
0
  free(arguments);
388
389
0
  for (i = 0; i < args_count(args); i++) {
390
0
    xsnprintf(tmp, sizeof tmp, "argument_%u", i);
391
0
    event_payload_set_string(ep, tmp, "%s", args_string(args, i));
392
0
  }
393
0
  flag = args_first(args, &ae);
394
0
  while (flag != 0) {
395
0
    value = args_get(args, flag);
396
0
    xsnprintf(tmp, sizeof tmp, "flag_%c", flag);
397
0
    if (value == NULL)
398
0
      event_payload_set_string(ep, tmp, "1");
399
0
    else
400
0
      event_payload_set_string(ep, tmp, "%s", value);
401
402
0
    i = 0;
403
0
    av = args_first_value(args, flag);
404
0
    while (av != NULL) {
405
0
      xsnprintf(tmp, sizeof tmp, "flag_%c_%u", flag, i);
406
0
      event_payload_set_string(ep, tmp, "%s", av->string);
407
0
      i++;
408
0
      av = args_next_value(av);
409
0
    }
410
411
0
    flag = args_next(&ae);
412
0
  }
413
414
0
  events_fire(name, ep);
415
0
  free(name);
416
0
}
417
418
/* Continue processing command queue. */
419
void
420
cmdq_continue(struct cmdq_item *item)
421
0
{
422
0
  item->flags &= ~CMDQ_WAITING;
423
0
}
424
425
/* Remove an item. */
426
static void
427
cmdq_remove(struct cmdq_item *item)
428
0
{
429
0
  if (item->client != NULL)
430
0
    server_client_unref(item->client);
431
0
  if (item->cmdlist != NULL)
432
0
    cmd_list_free(item->cmdlist);
433
0
  cmdq_free_state(item->state);
434
435
0
  TAILQ_REMOVE(&item->queue->list, item, entry);
436
437
0
  free(item->name);
438
0
  free(item);
439
0
}
440
441
/* Remove all subsequent items that match this item's group. */
442
static void
443
cmdq_remove_group(struct cmdq_item *item)
444
0
{
445
0
  struct cmdq_item  *this, *next;
446
447
0
  if (item->group == 0)
448
0
    return;
449
0
  this = TAILQ_NEXT(item, entry);
450
0
  while (this != NULL) {
451
0
    next = TAILQ_NEXT(this, entry);
452
0
    if (this->group == item->group)
453
0
      cmdq_remove(this);
454
0
    this = next;
455
0
  }
456
0
}
457
458
/* Empty command callback. */
459
static enum cmd_retval
460
cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data)
461
0
{
462
0
  return (CMD_RETURN_NORMAL);
463
0
}
464
465
/* Get a command for the command queue. */
466
struct cmdq_item *
467
cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
468
0
{
469
0
  struct cmdq_item  *item, *first = NULL, *last = NULL;
470
0
  struct cmd    *cmd;
471
0
  const struct cmd_entry  *entry;
472
0
  int      created = 0;
473
474
0
  if ((cmd = cmd_list_first(cmdlist)) == NULL)
475
0
    return (cmdq_get_callback(cmdq_empty_command, NULL));
476
477
0
  if (state == NULL) {
478
0
    state = cmdq_new_state(NULL, NULL, 0);
479
0
    created = 1;
480
0
  }
481
482
0
  while (cmd != NULL) {
483
0
    entry = cmd_get_entry(cmd);
484
485
0
    item = xcalloc(1, sizeof *item);
486
0
    xasprintf(&item->name, "[%s/%p]", entry->name, item);
487
0
    item->type = CMDQ_COMMAND;
488
489
0
    item->group = cmd_get_group(cmd);
490
0
    item->state = cmdq_link_state(state);
491
492
0
    item->cmdlist = cmdlist;
493
0
    item->cmd = cmd;
494
495
0
    cmdlist->references++;
496
0
    log_debug("%s: %s group %u", __func__, item->name, item->group);
497
498
0
    if (first == NULL)
499
0
      first = item;
500
0
    if (last != NULL)
501
0
      last->next = item;
502
0
    last = item;
503
504
0
    cmd = cmd_list_next(cmd);
505
0
  }
506
507
0
  if (created)
508
0
    cmdq_free_state(state);
509
0
  return (first);
510
0
}
511
512
/* Fill in flag for a command. */
513
static enum cmd_retval
514
cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
515
    const struct cmd_entry_flag *flag)
516
0
{
517
0
  const char  *value;
518
519
0
  if (flag->flag == 0) {
520
0
    cmd_find_from_client(fs, item->target_client, 0);
521
0
    return (CMD_RETURN_NORMAL);
522
0
  }
523
524
0
  value = args_get(cmd_get_args(item->cmd), flag->flag);
525
0
  if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
526
0
    cmd_find_clear_state(fs, 0);
527
0
    return (CMD_RETURN_ERROR);
528
0
  }
529
0
  return (CMD_RETURN_NORMAL);
530
0
}
531
532
/* Add message with command. */
533
static void
534
cmdq_add_message(struct cmdq_item *item)
535
0
{
536
0
  struct client   *c = item->client;
537
0
  struct cmdq_state *state = item->state;
538
0
  const char    *key;
539
0
  char      *tmp;
540
0
  uid_t                    uid;
541
0
  struct passwd   *pw;
542
0
  char                    *user = NULL;
543
544
0
  tmp = cmd_print(item->cmd);
545
0
  if (c != NULL) {
546
0
    uid = proc_get_peer_uid(c->peer);
547
0
    if (uid != (uid_t)-1 && uid != getuid()) {
548
0
      if ((pw = getpwuid(uid)) != NULL)
549
0
        xasprintf(&user, "[%s]", pw->pw_name);
550
0
      else
551
0
        user = xstrdup("[unknown]");
552
0
    } else
553
0
      user = xstrdup("");
554
0
    if (c->session != NULL && state->event.key != KEYC_NONE) {
555
0
      key = key_string_lookup_key(state->event.key, 0);
556
0
      server_add_message("%s%s key %s: %s", c->name, user,
557
0
          key, tmp);
558
0
    } else {
559
0
      server_add_message("%s%s command: %s", c->name, user,
560
0
          tmp);
561
0
    }
562
0
    free(user);
563
0
  } else
564
0
    server_add_message("command: %s", tmp);
565
0
  free(tmp);
566
0
}
567
568
/* Fire command on command queue. */
569
static enum cmd_retval
570
cmdq_fire_command(struct cmdq_item *item)
571
0
{
572
0
  const char    *name = cmdq_name(item->client);
573
0
  struct cmdq_state *state = item->state;
574
0
  struct cmd    *cmd = item->cmd;
575
0
  struct args   *args = cmd_get_args(cmd);
576
0
  const struct cmd_entry  *entry = cmd_get_entry(cmd);
577
0
  struct client   *tc, *saved = item->client;
578
0
  enum cmd_retval    retval;
579
0
  struct cmd_find_state *fsp, fs;
580
0
  int      flags, quiet = 0;
581
0
  char      *tmp;
582
583
0
  if (cfg_finished)
584
0
    cmdq_add_message(item);
585
0
  if (log_get_level() > 1) {
586
0
    tmp = cmd_print(cmd);
587
0
    log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
588
0
    free(tmp);
589
0
  }
590
591
0
  flags = !!(state->flags & CMDQ_STATE_CONTROL);
592
0
  cmdq_guard(item, "begin", flags);
593
594
0
  if (item->client == NULL)
595
0
    item->client = cmd_find_client(item, NULL, 1);
596
597
0
  if (entry->flags & CMD_CLIENT_CANFAIL)
598
0
    quiet = 1;
599
0
  if (entry->flags & CMD_CLIENT_CFLAG) {
600
0
    tc = cmd_find_client(item, args_get(args, 'c'), quiet);
601
0
    if (tc == NULL && !quiet) {
602
0
      retval = CMD_RETURN_ERROR;
603
0
      goto out;
604
0
    }
605
0
  } else if (entry->flags & CMD_CLIENT_TFLAG) {
606
0
    tc = cmd_find_client(item, args_get(args, 't'), quiet);
607
0
    if (tc == NULL && !quiet) {
608
0
      retval = CMD_RETURN_ERROR;
609
0
      goto out;
610
0
    }
611
0
  } else
612
0
    tc = cmd_find_client(item, NULL, 1);
613
0
  item->target_client = tc;
614
615
0
  retval = cmdq_find_flag(item, &item->source, &entry->source);
616
0
  if (retval == CMD_RETURN_ERROR)
617
0
    goto out;
618
0
  retval = cmdq_find_flag(item, &item->target, &entry->target);
619
0
  if (retval == CMD_RETURN_ERROR)
620
0
    goto out;
621
622
0
  retval = entry->exec(cmd, item);
623
0
  if (retval == CMD_RETURN_ERROR)
624
0
    goto out;
625
626
0
  if (entry->flags & CMD_AFTERHOOK) {
627
0
    if (cmd_find_valid_state(&item->target))
628
0
      fsp = &item->target;
629
0
    else if (cmd_find_valid_state(&item->state->current))
630
0
      fsp = &item->state->current;
631
0
    else if (cmd_find_from_client(&fs, item->client, 0) == 0)
632
0
      fsp = &fs;
633
0
    else
634
0
      goto out;
635
0
    cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
636
0
  }
637
638
0
out:
639
0
  item->client = saved;
640
0
  if (retval == CMD_RETURN_ERROR) {
641
0
    fsp = NULL;
642
0
    if (cmd_find_valid_state(&item->target))
643
0
      fsp = &item->target;
644
0
    else if (cmd_find_valid_state(&item->state->current))
645
0
      fsp = &item->state->current;
646
0
    else if (cmd_find_from_client(&fs, item->client, 0) == 0)
647
0
      fsp = &fs;
648
0
    cmdq_insert_hook(fsp != NULL ? fsp->s : NULL, item, fsp,
649
0
        "command-error");
650
0
    cmdq_guard(item, "error", flags);
651
0
  } else
652
0
    cmdq_guard(item, "end", flags);
653
0
  return (retval);
654
0
}
655
656
/* Get a callback for the command queue. */
657
struct cmdq_item *
658
cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
659
0
{
660
0
  struct cmdq_item  *item;
661
662
0
  item = xcalloc(1, sizeof *item);
663
0
  xasprintf(&item->name, "[%s/%p]", name, item);
664
0
  item->type = CMDQ_CALLBACK;
665
666
0
  item->group = 0;
667
0
  item->state = cmdq_new_state(NULL, NULL, 0);
668
669
0
  item->cb = cb;
670
0
  item->data = data;
671
672
0
  return (item);
673
0
}
674
675
/* Generic error callback. */
676
static enum cmd_retval
677
cmdq_error_callback(struct cmdq_item *item, void *data)
678
0
{
679
0
  char  *error = data;
680
681
0
  cmdq_error(item, "%s", error);
682
0
  free(error);
683
684
0
  return (CMD_RETURN_NORMAL);
685
0
}
686
687
/* Get an error callback for the command queue. */
688
struct cmdq_item *
689
cmdq_get_error(const char *error)
690
0
{
691
0
  return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
692
0
}
693
694
/* Fire callback on callback queue. */
695
static enum cmd_retval
696
cmdq_fire_callback(struct cmdq_item *item)
697
0
{
698
0
  return (item->cb(item, item->data));
699
0
}
700
701
/* Process next item on command queue. */
702
u_int
703
cmdq_next(struct client *c)
704
12.4k
{
705
12.4k
  struct cmdq_list  *queue = cmdq_get(c);
706
12.4k
  const char    *name = cmdq_name(c);
707
12.4k
  struct cmdq_item  *item;
708
12.4k
  enum cmd_retval    retval;
709
12.4k
  u_int      items = 0;
710
12.4k
  static u_int     number;
711
712
12.4k
  if (TAILQ_EMPTY(&queue->list)) {
713
12.4k
    log_debug("%s %s: empty", __func__, name);
714
12.4k
    return (0);
715
12.4k
  }
716
0
  if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
717
0
    log_debug("%s %s: waiting", __func__, name);
718
0
    return (0);
719
0
  }
720
721
0
  log_debug("%s %s: enter", __func__, name);
722
0
  for (;;) {
723
0
    item = queue->item = TAILQ_FIRST(&queue->list);
724
0
    if (item == NULL)
725
0
      break;
726
0
    log_debug("%s %s: %s (%d), flags %x", __func__, name,
727
0
        item->name, item->type, item->flags);
728
729
    /*
730
     * Any item with the waiting flag set waits until an external
731
     * event clears the flag (for example, a job - look at
732
     * run-shell).
733
     */
734
0
    if (item->flags & CMDQ_WAITING)
735
0
      goto waiting;
736
737
    /*
738
     * Items are only fired once, once the fired flag is set, a
739
     * waiting flag can only be cleared by an external event.
740
     */
741
0
    if (~item->flags & CMDQ_FIRED) {
742
0
      item->time = time(NULL);
743
0
      item->number = ++number;
744
745
0
      switch (item->type) {
746
0
      case CMDQ_COMMAND:
747
0
        retval = cmdq_fire_command(item);
748
749
        /*
750
         * If a command returns an error, remove any
751
         * subsequent commands in the same group.
752
         */
753
0
        if (retval == CMD_RETURN_ERROR)
754
0
          cmdq_remove_group(item);
755
0
        break;
756
0
      case CMDQ_CALLBACK:
757
0
        retval = cmdq_fire_callback(item);
758
0
        break;
759
0
      default:
760
0
        retval = CMD_RETURN_ERROR;
761
0
        break;
762
0
      }
763
0
      item->flags |= CMDQ_FIRED;
764
765
0
      if (retval == CMD_RETURN_WAIT) {
766
0
        item->flags |= CMDQ_WAITING;
767
0
        goto waiting;
768
0
      }
769
0
      items++;
770
0
    }
771
0
    cmdq_remove(item);
772
0
  }
773
0
  queue->item = NULL;
774
775
0
  log_debug("%s %s: exit (empty)", __func__, name);
776
0
  return (items);
777
778
0
waiting:
779
0
  log_debug("%s %s: exit (wait)", __func__, name);
780
0
  return (items);
781
0
}
782
783
/* Get running item if any. */
784
struct cmdq_item *
785
cmdq_running(struct client *c)
786
0
{
787
0
  struct cmdq_list  *queue = cmdq_get(c);
788
789
0
  if (queue->item == NULL)
790
0
    return (NULL);
791
0
  if (queue->item->flags & CMDQ_WAITING)
792
0
    return (NULL);
793
0
  return (queue->item);
794
0
}
795
796
/* Print a guard line. */
797
void
798
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
799
0
{
800
0
  struct client *c = item->client;
801
0
  long     t = item->time;
802
0
  u_int    number = item->number;
803
804
0
  if (c != NULL && (c->flags & CLIENT_CONTROL))
805
0
    control_write_guard(c, guard, t, number, flags);
806
0
}
807
808
/* Show message from command. */
809
void
810
cmdq_print_data(struct cmdq_item *item, struct evbuffer *evb)
811
0
{
812
0
  server_client_print(item->client, 1, evb);
813
0
}
814
815
/* Show message from command. */
816
void
817
cmdq_print(struct cmdq_item *item, const char *fmt, ...)
818
0
{
819
0
  va_list    ap;
820
0
  struct evbuffer *evb;
821
822
0
  evb = evbuffer_new();
823
0
  if (evb == NULL)
824
0
    fatalx("out of memory");
825
826
0
  va_start(ap, fmt);
827
0
  evbuffer_add_vprintf(evb, fmt, ap);
828
0
  va_end(ap);
829
830
0
  cmdq_print_data(item, evb);
831
0
  evbuffer_free(evb);
832
0
}
833
834
/* Show error from command. */
835
void
836
cmdq_error(struct cmdq_item *item, const char *fmt, ...)
837
0
{
838
0
  struct client *c = item->client;
839
0
  struct cmd  *cmd = item->cmd;
840
0
  va_list    ap;
841
0
  char    *msg, *tmp;
842
0
  const char  *file;
843
0
  u_int    line;
844
845
0
  va_start(ap, fmt);
846
0
  xvasprintf(&msg, fmt, ap);
847
0
  va_end(ap);
848
849
0
  log_debug("%s: %s", __func__, msg);
850
851
0
  if (c == NULL) {
852
0
    cmd_get_source(cmd, &file, &line);
853
0
    if (!cfg_finished) {
854
0
      if (file != NULL)
855
0
        cfg_add_cause("%s:%u: %s", file, line, msg);
856
0
      else
857
0
        cfg_add_cause("%s", msg);
858
0
    } else {
859
0
      if (file != NULL) {
860
0
        server_add_message("message: %s:%u: %s", file,
861
0
            line, msg);
862
0
      } else
863
0
        server_add_message("message: %s", msg);
864
0
    }
865
0
  } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
866
0
    server_add_message("%s message: %s", c->name, msg);
867
0
    if (~c->flags & CLIENT_UTF8) {
868
0
      tmp = msg;
869
0
      msg = utf8_sanitize(tmp);
870
0
      free(tmp);
871
0
    }
872
0
    if (c->flags & CLIENT_CONTROL)
873
0
      control_write(c, "%s", msg);
874
0
    else
875
0
      file_error(c, "%s\n", msg);
876
0
    c->retval = 1;
877
0
  } else {
878
0
    *msg = toupper((u_char) *msg);
879
0
    status_message_set(c, -1, 1, 0, 0, "%s", msg);
880
0
  }
881
882
0
  free(msg);
883
0
}