Coverage Report

Created: 2024-09-11 06:44

/src/tmux/cmd-queue.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD$ */
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
16.8k
#define CMDQ_FIRED 0x1
32
8.85k
#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
20.5k
{
98
20.5k
  static char s[256];
99
100
20.5k
  if (c == NULL)
101
20.5k
    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
20.5k
}
108
109
/* Get command queue from client. */
110
static struct cmdq_list *
111
cmdq_get(struct client *c)
112
28.9k
{
113
28.9k
  static struct cmdq_list *global_queue;
114
115
28.9k
  if (c == NULL) {
116
28.9k
    if (global_queue == NULL)
117
1
      global_queue = cmdq_new();
118
28.9k
    return (global_queue);
119
28.9k
  }
120
0
  return (c->queue);
121
28.9k
}
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 client. */
151
struct client *
152
cmdq_get_client(struct cmdq_item *item)
153
0
{
154
0
  return (item->client);
155
0
}
156
157
/* Get item target client. */
158
struct client *
159
cmdq_get_target_client(struct cmdq_item *item)
160
0
{
161
0
  return (item->target_client);
162
0
}
163
164
/* Get item state. */
165
struct cmdq_state *
166
cmdq_get_state(struct cmdq_item *item)
167
0
{
168
0
  return (item->state);
169
0
}
170
171
/* Get item target. */
172
struct cmd_find_state *
173
cmdq_get_target(struct cmdq_item *item)
174
0
{
175
0
  return (&item->target);
176
0
}
177
178
/* Get item source. */
179
struct cmd_find_state *
180
cmdq_get_source(struct cmdq_item *item)
181
0
{
182
0
  return (&item->source);
183
0
}
184
185
/* Get state event. */
186
struct key_event *
187
cmdq_get_event(struct cmdq_item *item)
188
0
{
189
0
  return (&item->state->event);
190
0
}
191
192
/* Get state current target. */
193
struct cmd_find_state *
194
cmdq_get_current(struct cmdq_item *item)
195
0
{
196
0
  return (&item->state->current);
197
0
}
198
199
/* Get state flags. */
200
int
201
cmdq_get_flags(struct cmdq_item *item)
202
0
{
203
0
  return (item->state->flags);
204
0
}
205
206
/* Create a new state. */
207
struct cmdq_state *
208
cmdq_new_state(struct cmd_find_state *current, struct key_event *event,
209
    int flags)
210
8.43k
{
211
8.43k
  struct cmdq_state *state;
212
213
8.43k
  state = xcalloc(1, sizeof *state);
214
8.43k
  state->references = 1;
215
8.43k
  state->flags = flags;
216
217
8.43k
  if (event != NULL)
218
0
    memcpy(&state->event, event, sizeof state->event);
219
8.43k
  else
220
8.43k
    state->event.key = KEYC_NONE;
221
8.43k
  if (current != NULL && cmd_find_valid_state(current))
222
0
    cmd_find_copy_state(&state->current, current);
223
8.43k
  else
224
8.43k
    cmd_find_clear_state(&state->current, 0);
225
226
8.43k
  return (state);
227
8.43k
}
228
229
/* Add a reference to a state. */
230
struct cmdq_state *
231
cmdq_link_state(struct cmdq_state *state)
232
0
{
233
0
  state->references++;
234
0
  return (state);
235
0
}
236
237
/* Make a copy of a state. */
238
struct cmdq_state *
239
cmdq_copy_state(struct cmdq_state *state, struct cmd_find_state *current)
240
0
{
241
0
  if (current != NULL)
242
0
    return (cmdq_new_state(current, &state->event, state->flags));
243
0
  return (cmdq_new_state(&state->current, &state->event, state->flags));
244
0
}
245
246
/* Free a state. */
247
void
248
cmdq_free_state(struct cmdq_state *state)
249
8.43k
{
250
8.43k
  if (--state->references != 0)
251
0
    return;
252
253
8.43k
  if (state->formats != NULL)
254
0
    format_free(state->formats);
255
8.43k
  free(state);
256
8.43k
}
257
258
/* Add a format to command queue. */
259
void
260
cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
261
0
{
262
0
  va_list  ap;
263
0
  char  *value;
264
265
0
  va_start(ap, fmt);
266
0
  xvasprintf(&value, fmt, ap);
267
0
  va_end(ap);
268
269
0
  if (state->formats == NULL)
270
0
    state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
271
0
  format_add(state->formats, key, "%s", value);
272
273
0
  free(value);
274
0
}
275
276
/* Add formats to command queue. */
277
void
278
cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft)
279
0
{
280
0
  if (state->formats == NULL)
281
0
    state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
282
0
  format_merge(state->formats, ft);
283
0
}
284
285
/* Merge formats from item. */
286
void
287
cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
288
0
{
289
0
  const struct cmd_entry  *entry;
290
291
0
  if (item->cmd != NULL) {
292
0
    entry = cmd_get_entry(item->cmd);
293
0
    format_add(ft, "command", "%s", entry->name);
294
0
  }
295
0
  if (item->state->formats != NULL)
296
0
    format_merge(ft, item->state->formats);
297
0
}
298
299
/* Append an item. */
300
struct cmdq_item *
301
cmdq_append(struct client *c, struct cmdq_item *item)
302
8.43k
{
303
8.43k
  struct cmdq_list  *queue = cmdq_get(c);
304
8.43k
  struct cmdq_item  *next;
305
306
8.43k
  do {
307
8.43k
    next = item->next;
308
8.43k
    item->next = NULL;
309
310
8.43k
    if (c != NULL)
311
0
      c->references++;
312
8.43k
    item->client = c;
313
314
8.43k
    item->queue = queue;
315
8.43k
    TAILQ_INSERT_TAIL(&queue->list, item, entry);
316
8.43k
    log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
317
318
8.43k
    item = next;
319
8.43k
  } while (item != NULL);
320
8.43k
  return (TAILQ_LAST(&queue->list, cmdq_item_list));
321
8.43k
}
322
323
/* Insert an item. */
324
struct cmdq_item *
325
cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
326
0
{
327
0
  struct client   *c = after->client;
328
0
  struct cmdq_list  *queue = after->queue;
329
0
  struct cmdq_item  *next;
330
331
0
  do {
332
0
    next = item->next;
333
0
    item->next = after->next;
334
0
    after->next = item;
335
336
0
    if (c != NULL)
337
0
      c->references++;
338
0
    item->client = c;
339
340
0
    item->queue = queue;
341
0
    TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
342
0
    log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
343
0
        item->name, after->name);
344
345
0
    after = item;
346
0
    item = next;
347
0
  } while (item != NULL);
348
0
  return (after);
349
0
}
350
351
/* Insert a hook. */
352
void
353
cmdq_insert_hook(struct session *s, struct cmdq_item *item,
354
    struct cmd_find_state *current, const char *fmt, ...)
355
0
{
356
0
  struct cmdq_state   *state = item->state;
357
0
  struct cmd      *cmd = item->cmd;
358
0
  struct args     *args = cmd_get_args(cmd);
359
0
  struct args_entry   *ae;
360
0
  struct args_value   *av;
361
0
  struct options      *oo;
362
0
  va_list        ap;
363
0
  char        *name, tmp[32], flag, *arguments;
364
0
  u_int        i;
365
0
  const char      *value;
366
0
  struct cmdq_item    *new_item;
367
0
  struct cmdq_state   *new_state;
368
0
  struct options_entry    *o;
369
0
  struct options_array_item *a;
370
0
  struct cmd_list     *cmdlist;
371
372
0
  if (item->state->flags & CMDQ_STATE_NOHOOKS)
373
0
    return;
374
0
  if (s == NULL)
375
0
    oo = global_s_options;
376
0
  else
377
0
    oo = s->options;
378
379
0
  va_start(ap, fmt);
380
0
  xvasprintf(&name, fmt, ap);
381
0
  va_end(ap);
382
383
0
  o = options_get(oo, name);
384
0
  if (o == NULL) {
385
0
    free(name);
386
0
    return;
387
0
  }
388
0
  log_debug("running hook %s (parent %p)", name, item);
389
390
  /*
391
   * The hooks get a new state because they should not update the current
392
   * target or formats for any subsequent commands.
393
   */
394
0
  new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
395
0
  cmdq_add_format(new_state, "hook", "%s", name);
396
397
0
  arguments = args_print(args);
398
0
  cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
399
0
  free(arguments);
400
401
0
  for (i = 0; i < args_count(args); i++) {
402
0
    xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
403
0
    cmdq_add_format(new_state, tmp, "%s", args_string(args, i));
404
0
  }
405
0
  flag = args_first(args, &ae);
406
0
  while (flag != 0) {
407
0
    value = args_get(args, flag);
408
0
    if (value == NULL) {
409
0
      xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
410
0
      cmdq_add_format(new_state, tmp, "1");
411
0
    } else {
412
0
      xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
413
0
      cmdq_add_format(new_state, tmp, "%s", value);
414
0
    }
415
416
0
    i = 0;
417
0
    av = args_first_value(args, flag);
418
0
    while (av != NULL) {
419
0
      xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
420
0
      cmdq_add_format(new_state, tmp, "%s", av->string);
421
0
      i++;
422
0
      av = args_next_value(av);
423
0
    }
424
425
0
    flag = args_next(&ae);
426
0
  }
427
428
0
  a = options_array_first(o);
429
0
  while (a != NULL) {
430
0
    cmdlist = options_array_item_value(a)->cmdlist;
431
0
    if (cmdlist != NULL) {
432
0
      new_item = cmdq_get_command(cmdlist, new_state);
433
0
      if (item != NULL)
434
0
        item = cmdq_insert_after(item, new_item);
435
0
      else
436
0
        item = cmdq_append(NULL, new_item);
437
0
    }
438
0
    a = options_array_next(a);
439
0
  }
440
441
0
  cmdq_free_state(new_state);
442
0
  free(name);
443
0
}
444
445
/* Continue processing command queue. */
446
void
447
cmdq_continue(struct cmdq_item *item)
448
0
{
449
0
  item->flags &= ~CMDQ_WAITING;
450
0
}
451
452
/* Remove an item. */
453
static void
454
cmdq_remove(struct cmdq_item *item)
455
8.43k
{
456
8.43k
  if (item->client != NULL)
457
0
    server_client_unref(item->client);
458
8.43k
  if (item->cmdlist != NULL)
459
0
    cmd_list_free(item->cmdlist);
460
8.43k
  cmdq_free_state(item->state);
461
462
8.43k
  TAILQ_REMOVE(&item->queue->list, item, entry);
463
464
8.43k
  free(item->name);
465
8.43k
  free(item);
466
8.43k
}
467
468
/* Remove all subsequent items that match this item's group. */
469
static void
470
cmdq_remove_group(struct cmdq_item *item)
471
0
{
472
0
  struct cmdq_item  *this, *next;
473
474
0
  if (item->group == 0)
475
0
    return;
476
0
  this = TAILQ_NEXT(item, entry);
477
0
  while (this != NULL) {
478
0
    next = TAILQ_NEXT(this, entry);
479
0
    if (this->group == item->group)
480
0
      cmdq_remove(this);
481
0
    this = next;
482
0
  }
483
0
}
484
485
/* Empty command callback. */
486
static enum cmd_retval
487
cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data)
488
0
{
489
0
  return (CMD_RETURN_NORMAL);
490
0
}
491
492
/* Get a command for the command queue. */
493
struct cmdq_item *
494
cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
495
0
{
496
0
  struct cmdq_item  *item, *first = NULL, *last = NULL;
497
0
  struct cmd    *cmd;
498
0
  const struct cmd_entry  *entry;
499
0
  int      created = 0;
500
501
0
  if ((cmd = cmd_list_first(cmdlist)) == NULL)
502
0
    return (cmdq_get_callback(cmdq_empty_command, NULL));
503
504
0
  if (state == NULL) {
505
0
    state = cmdq_new_state(NULL, NULL, 0);
506
0
    created = 1;
507
0
  }
508
509
0
  while (cmd != NULL) {
510
0
    entry = cmd_get_entry(cmd);
511
512
0
    item = xcalloc(1, sizeof *item);
513
0
    xasprintf(&item->name, "[%s/%p]", entry->name, item);
514
0
    item->type = CMDQ_COMMAND;
515
516
0
    item->group = cmd_get_group(cmd);
517
0
    item->state = cmdq_link_state(state);
518
519
0
    item->cmdlist = cmdlist;
520
0
    item->cmd = cmd;
521
522
0
    cmdlist->references++;
523
0
    log_debug("%s: %s group %u", __func__, item->name, item->group);
524
525
0
    if (first == NULL)
526
0
      first = item;
527
0
    if (last != NULL)
528
0
      last->next = item;
529
0
    last = item;
530
531
0
    cmd = cmd_list_next(cmd);
532
0
  }
533
534
0
  if (created)
535
0
    cmdq_free_state(state);
536
0
  return (first);
537
0
}
538
539
/* Fill in flag for a command. */
540
static enum cmd_retval
541
cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
542
    const struct cmd_entry_flag *flag)
543
0
{
544
0
  const char  *value;
545
546
0
  if (flag->flag == 0) {
547
0
    cmd_find_from_client(fs, item->target_client, 0);
548
0
    return (CMD_RETURN_NORMAL);
549
0
  }
550
551
0
  value = args_get(cmd_get_args(item->cmd), flag->flag);
552
0
  if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
553
0
    cmd_find_clear_state(fs, 0);
554
0
    return (CMD_RETURN_ERROR);
555
0
  }
556
0
  return (CMD_RETURN_NORMAL);
557
0
}
558
559
/* Add message with command. */
560
static void
561
cmdq_add_message(struct cmdq_item *item)
562
0
{
563
0
  struct client   *c = item->client;
564
0
  struct cmdq_state *state = item->state;
565
0
  const char    *key;
566
0
  char      *tmp;
567
0
  uid_t                    uid;
568
0
  struct passwd   *pw;
569
0
  char                    *user = NULL;
570
571
0
  tmp = cmd_print(item->cmd);
572
0
  if (c != NULL) {
573
0
    uid = proc_get_peer_uid(c->peer);
574
0
    if (uid != (uid_t)-1 && uid != getuid()) {
575
0
      if ((pw = getpwuid(uid)) != NULL)
576
0
        xasprintf(&user, "[%s]", pw->pw_name);
577
0
      else
578
0
        user = xstrdup("[unknown]");
579
0
    } else
580
0
      user = xstrdup("");
581
0
    if (c->session != NULL && state->event.key != KEYC_NONE) {
582
0
      key = key_string_lookup_key(state->event.key, 0);
583
0
      server_add_message("%s%s key %s: %s", c->name, user,
584
0
          key, tmp);
585
0
    } else {
586
0
      server_add_message("%s%s command: %s", c->name, user,
587
0
          tmp);
588
0
    }
589
0
    free(user);
590
0
  } else
591
0
    server_add_message("command: %s", tmp);
592
0
  free(tmp);
593
0
}
594
595
/* Fire command on command queue. */
596
static enum cmd_retval
597
cmdq_fire_command(struct cmdq_item *item)
598
0
{
599
0
  const char    *name = cmdq_name(item->client);
600
0
  struct cmdq_state *state = item->state;
601
0
  struct cmd    *cmd = item->cmd;
602
0
  struct args   *args = cmd_get_args(cmd);
603
0
  const struct cmd_entry  *entry = cmd_get_entry(cmd);
604
0
  struct client   *tc, *saved = item->client;
605
0
  enum cmd_retval    retval;
606
0
  struct cmd_find_state *fsp, fs;
607
0
  int      flags, quiet = 0;
608
0
  char      *tmp;
609
610
0
  if (cfg_finished)
611
0
    cmdq_add_message(item);
612
0
  if (log_get_level() > 1) {
613
0
    tmp = cmd_print(cmd);
614
0
    log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
615
0
    free(tmp);
616
0
  }
617
618
0
  flags = !!(state->flags & CMDQ_STATE_CONTROL);
619
0
  cmdq_guard(item, "begin", flags);
620
621
0
  if (item->client == NULL)
622
0
    item->client = cmd_find_client(item, NULL, 1);
623
624
0
  if (entry->flags & CMD_CLIENT_CANFAIL)
625
0
    quiet = 1;
626
0
  if (entry->flags & CMD_CLIENT_CFLAG) {
627
0
    tc = cmd_find_client(item, args_get(args, 'c'), quiet);
628
0
    if (tc == NULL && !quiet) {
629
0
      retval = CMD_RETURN_ERROR;
630
0
      goto out;
631
0
    }
632
0
  } else if (entry->flags & CMD_CLIENT_TFLAG) {
633
0
    tc = cmd_find_client(item, args_get(args, 't'), quiet);
634
0
    if (tc == NULL && !quiet) {
635
0
      retval = CMD_RETURN_ERROR;
636
0
      goto out;
637
0
    }
638
0
  } else
639
0
    tc = cmd_find_client(item, NULL, 1);
640
0
  item->target_client = tc;
641
642
0
  retval = cmdq_find_flag(item, &item->source, &entry->source);
643
0
  if (retval == CMD_RETURN_ERROR)
644
0
    goto out;
645
0
  retval = cmdq_find_flag(item, &item->target, &entry->target);
646
0
  if (retval == CMD_RETURN_ERROR)
647
0
    goto out;
648
649
0
  retval = entry->exec(cmd, item);
650
0
  if (retval == CMD_RETURN_ERROR)
651
0
    goto out;
652
653
0
  if (entry->flags & CMD_AFTERHOOK) {
654
0
    if (cmd_find_valid_state(&item->target))
655
0
      fsp = &item->target;
656
0
    else if (cmd_find_valid_state(&item->state->current))
657
0
      fsp = &item->state->current;
658
0
    else if (cmd_find_from_client(&fs, item->client, 0) == 0)
659
0
      fsp = &fs;
660
0
    else
661
0
      goto out;
662
0
    cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
663
0
  }
664
665
0
out:
666
0
  item->client = saved;
667
0
  if (retval == CMD_RETURN_ERROR) {
668
0
    fsp = NULL;
669
0
    if (cmd_find_valid_state(&item->target))
670
0
      fsp = &item->target;
671
0
    else if (cmd_find_valid_state(&item->state->current))
672
0
      fsp = &item->state->current;
673
0
    else if (cmd_find_from_client(&fs, item->client, 0) == 0)
674
0
      fsp = &fs;
675
0
    cmdq_insert_hook(fsp != NULL ? fsp->s : NULL, item, fsp,
676
0
        "command-error");
677
0
    cmdq_guard(item, "error", flags);
678
0
  } else
679
0
    cmdq_guard(item, "end", flags);
680
0
  return (retval);
681
0
}
682
683
/* Get a callback for the command queue. */
684
struct cmdq_item *
685
cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
686
8.43k
{
687
8.43k
  struct cmdq_item  *item;
688
689
8.43k
  item = xcalloc(1, sizeof *item);
690
8.43k
  xasprintf(&item->name, "[%s/%p]", name, item);
691
8.43k
  item->type = CMDQ_CALLBACK;
692
693
8.43k
  item->group = 0;
694
8.43k
  item->state = cmdq_new_state(NULL, NULL, 0);
695
696
8.43k
  item->cb = cb;
697
8.43k
  item->data = data;
698
699
8.43k
  return (item);
700
8.43k
}
701
702
/* Generic error callback. */
703
static enum cmd_retval
704
cmdq_error_callback(struct cmdq_item *item, void *data)
705
0
{
706
0
  char  *error = data;
707
708
0
  cmdq_error(item, "%s", error);
709
0
  free(error);
710
711
0
  return (CMD_RETURN_NORMAL);
712
0
}
713
714
/* Get an error callback for the command queue. */
715
struct cmdq_item *
716
cmdq_get_error(const char *error)
717
0
{
718
0
  return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
719
0
}
720
721
/* Fire callback on callback queue. */
722
static enum cmd_retval
723
cmdq_fire_callback(struct cmdq_item *item)
724
8.43k
{
725
8.43k
  return (item->cb(item, item->data));
726
8.43k
}
727
728
/* Process next item on command queue. */
729
u_int
730
cmdq_next(struct client *c)
731
12.1k
{
732
12.1k
  struct cmdq_list  *queue = cmdq_get(c);
733
12.1k
  const char    *name = cmdq_name(c);
734
12.1k
  struct cmdq_item  *item;
735
12.1k
  enum cmd_retval    retval;
736
12.1k
  u_int      items = 0;
737
12.1k
  static u_int     number;
738
739
12.1k
  if (TAILQ_EMPTY(&queue->list)) {
740
11.6k
    log_debug("%s %s: empty", __func__, name);
741
11.6k
    return (0);
742
11.6k
  }
743
424
  if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
744
0
    log_debug("%s %s: waiting", __func__, name);
745
0
    return (0);
746
0
  }
747
748
424
  log_debug("%s %s: enter", __func__, name);
749
8.85k
  for (;;) {
750
8.85k
    item = queue->item = TAILQ_FIRST(&queue->list);
751
8.85k
    if (item == NULL)
752
424
      break;
753
8.43k
    log_debug("%s %s: %s (%d), flags %x", __func__, name,
754
8.43k
        item->name, item->type, item->flags);
755
756
    /*
757
     * Any item with the waiting flag set waits until an external
758
     * event clears the flag (for example, a job - look at
759
     * run-shell).
760
     */
761
8.43k
    if (item->flags & CMDQ_WAITING)
762
0
      goto waiting;
763
764
    /*
765
     * Items are only fired once, once the fired flag is set, a
766
     * waiting flag can only be cleared by an external event.
767
     */
768
8.43k
    if (~item->flags & CMDQ_FIRED) {
769
8.43k
      item->time = time(NULL);
770
8.43k
      item->number = ++number;
771
772
8.43k
      switch (item->type) {
773
0
      case CMDQ_COMMAND:
774
0
        retval = cmdq_fire_command(item);
775
776
        /*
777
         * If a command returns an error, remove any
778
         * subsequent commands in the same group.
779
         */
780
0
        if (retval == CMD_RETURN_ERROR)
781
0
          cmdq_remove_group(item);
782
0
        break;
783
8.43k
      case CMDQ_CALLBACK:
784
8.43k
        retval = cmdq_fire_callback(item);
785
8.43k
        break;
786
0
      default:
787
0
        retval = CMD_RETURN_ERROR;
788
0
        break;
789
8.43k
      }
790
8.43k
      item->flags |= CMDQ_FIRED;
791
792
8.43k
      if (retval == CMD_RETURN_WAIT) {
793
0
        item->flags |= CMDQ_WAITING;
794
0
        goto waiting;
795
0
      }
796
8.43k
      items++;
797
8.43k
    }
798
8.43k
    cmdq_remove(item);
799
8.43k
  }
800
424
  queue->item = NULL;
801
802
424
  log_debug("%s %s: exit (empty)", __func__, name);
803
424
  return (items);
804
805
0
waiting:
806
0
  log_debug("%s %s: exit (wait)", __func__, name);
807
0
  return (items);
808
424
}
809
810
/* Get running item if any. */
811
struct cmdq_item *
812
cmdq_running(struct client *c)
813
8.43k
{
814
8.43k
  struct cmdq_list  *queue = cmdq_get(c);
815
816
8.43k
  if (queue->item == NULL)
817
8.43k
    return (NULL);
818
0
  if (queue->item->flags & CMDQ_WAITING)
819
0
    return (NULL);
820
0
  return (queue->item);
821
0
}
822
823
/* Print a guard line. */
824
void
825
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
826
0
{
827
0
  struct client *c = item->client;
828
0
  long     t = item->time;
829
0
  u_int    number = item->number;
830
831
0
  if (c != NULL && (c->flags & CLIENT_CONTROL))
832
0
    control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
833
0
}
834
835
/* Show message from command. */
836
void
837
cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
838
0
{
839
0
  server_client_print(item->client, parse, evb);
840
0
}
841
842
/* Show message from command. */
843
void
844
cmdq_print(struct cmdq_item *item, const char *fmt, ...)
845
0
{
846
0
  va_list    ap;
847
0
  struct evbuffer *evb;
848
849
0
  evb = evbuffer_new();
850
0
  if (evb == NULL)
851
0
    fatalx("out of memory");
852
853
0
  va_start(ap, fmt);
854
0
  evbuffer_add_vprintf(evb, fmt, ap);
855
0
  va_end(ap);
856
857
0
  cmdq_print_data(item, 0, evb);
858
0
  evbuffer_free(evb);
859
0
}
860
861
/* Show error from command. */
862
void
863
cmdq_error(struct cmdq_item *item, const char *fmt, ...)
864
0
{
865
0
  struct client *c = item->client;
866
0
  struct cmd  *cmd = item->cmd;
867
0
  va_list    ap;
868
0
  char    *msg, *tmp;
869
0
  const char  *file;
870
0
  u_int    line;
871
872
0
  va_start(ap, fmt);
873
0
  xvasprintf(&msg, fmt, ap);
874
0
  va_end(ap);
875
876
0
  log_debug("%s: %s", __func__, msg);
877
878
0
  if (c == NULL) {
879
0
    cmd_get_source(cmd, &file, &line);
880
0
    cfg_add_cause("%s:%u: %s", file, line, msg);
881
0
  } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
882
0
    server_add_message("%s message: %s", c->name, msg);
883
0
    if (~c->flags & CLIENT_UTF8) {
884
0
      tmp = msg;
885
0
      msg = utf8_sanitize(tmp);
886
0
      free(tmp);
887
0
    }
888
0
    if (c->flags & CLIENT_CONTROL)
889
0
      control_write(c, "%s", msg);
890
0
    else
891
0
      file_error(c, "%s\n", msg);
892
0
    c->retval = 1;
893
0
  } else {
894
0
    *msg = toupper((u_char) *msg);
895
0
    status_message_set(c, -1, 1, 0, "%s", msg);
896
0
  }
897
898
0
  free(msg);
899
0
}