@@ -2,10 +2,6 @@ use clap::{CommandFactory, FromArgMatches};
2
2
use clap_complete:: generate;
3
3
use cli_table:: ColorChoice ;
4
4
use colored:: Colorize ;
5
- use std:: io;
6
- use std:: io:: IsTerminal ;
7
- use std:: panic:: PanicHookInfo ;
8
-
9
5
use hyperqueue:: HQ_VERSION ;
10
6
use hyperqueue:: client:: commands:: autoalloc:: command_autoalloc;
11
7
use hyperqueue:: client:: commands:: doc:: command_doc;
@@ -16,7 +12,7 @@ use hyperqueue::client::commands::job::{
16
12
} ;
17
13
use hyperqueue:: client:: commands:: journal:: command_journal;
18
14
use hyperqueue:: client:: commands:: outputlog:: command_reader;
19
- use hyperqueue:: client:: commands:: server:: command_server;
15
+ use hyperqueue:: client:: commands:: server:: { ServerCommand , ServerOpts , command_server} ;
20
16
use hyperqueue:: client:: commands:: submit:: command:: { SubmitJobConfOpts , open_job} ;
21
17
use hyperqueue:: client:: commands:: submit:: {
22
18
JobSubmitFileOpts , JobSubmitOpts , submit_computation, submit_computation_from_job_file,
@@ -40,7 +36,7 @@ use hyperqueue::client::task::{
40
36
use hyperqueue:: common:: cli:: {
41
37
ColorPolicy , CommonOpts , DeploySshOpts , GenerateCompletionOpts , HwDetectOpts , JobCommand ,
42
38
JobProgressOpts , JobWaitOpts , OptsWithMatches , RootOptions , SubCommand , WorkerAddressOpts ,
43
- WorkerCommand , WorkerInfoOpts , WorkerListOpts , WorkerStopOpts , WorkerWaitOpts ,
39
+ WorkerCommand , WorkerInfoOpts , WorkerListOpts , WorkerOpts , WorkerStopOpts , WorkerWaitOpts ,
44
40
get_task_id_selector, get_task_selector,
45
41
} ;
46
42
use hyperqueue:: common:: setup:: setup_logging;
@@ -52,6 +48,10 @@ use hyperqueue::transfer::messages::{
52
48
use hyperqueue:: worker:: hwdetect:: {
53
49
detect_additional_resources, detect_cpus, prune_hyper_threading,
54
50
} ;
51
+ use nix:: sys:: signal:: { SigHandler , Signal } ;
52
+ use std:: io;
53
+ use std:: io:: IsTerminal ;
54
+ use std:: panic:: PanicHookInfo ;
55
55
use tako:: resources:: { CPU_RESOURCE_NAME , ResourceDescriptor , ResourceDescriptorItem } ;
56
56
57
57
#[ cfg( feature = "jemalloc" ) ]
@@ -382,6 +382,17 @@ environment variable, and attach the logs to the issue, to provide us more infor
382
382
} ;
383
383
}
384
384
385
+ #[ cfg( unix) ]
386
+ fn reset_sigpipe ( ) {
387
+ unsafe {
388
+ nix:: sys:: signal:: signal ( Signal :: SIGPIPE , SigHandler :: SigDfl )
389
+ . expect ( "cannot reset sigpipe" ) ;
390
+ }
391
+ }
392
+
393
+ #[ cfg( not( unix) ) ]
394
+ fn reset_sigpipe ( ) { }
395
+
385
396
#[ tokio:: main( flavor = "current_thread" ) ]
386
397
async fn main ( ) -> hyperqueue:: Result < ( ) > {
387
398
// Augment panics - first print the error and backtrace like normally,
@@ -419,6 +430,35 @@ async fn main() -> hyperqueue::Result<()> {
419
430
420
431
let gsettings = make_global_settings ( top_opts. common ) ;
421
432
433
+ let is_cli_like = match & top_opts. subcmd {
434
+ SubCommand :: Server ( ServerOpts {
435
+ subcmd : ServerCommand :: Start ( _) ,
436
+ } ) => false ,
437
+ SubCommand :: Worker ( WorkerOpts {
438
+ subcmd : WorkerCommand :: Start ( _) ,
439
+ } ) => false ,
440
+ #[ cfg( feature = "dashboard" ) ]
441
+ SubCommand :: Dashboard ( _) => false ,
442
+ _ => true ,
443
+ } ;
444
+
445
+ if is_cli_like {
446
+ // When our stdout is attached to a pipe and the pipe is closed,
447
+ // it manifests as an I/O error, because the Rust runtime ignores
448
+ // SIGPIPE by default.
449
+ // This in turn causes `println!` to panic, which is not ideal,
450
+ // because it crashes HQ when used with Unix CLI utilities (such as `head`).
451
+ // Therefore, we reset SIGPIPE to its default behavior (terminate the process)
452
+ // to avoid the panics.
453
+ // See https://github.com/It4innovations/hyperqueue/issues/851.
454
+ // However, we only do this for client commands, which are short running and
455
+ // designed to be combined with other CLI tools.
456
+ // Enabling this for server and workers has unintended consequences, for example
457
+ // when a worker writes stdin to a task and the task has closed its stdin, then
458
+ // this would terminate the worker.
459
+ reset_sigpipe ( ) ;
460
+ }
461
+
422
462
let result = match top_opts. subcmd {
423
463
SubCommand :: Server ( opts) => command_server ( & gsettings, opts) . await ,
424
464
SubCommand :: Worker ( opts) => match opts. subcmd {
0 commit comments