383 equal(System.getenv("PATH"), null);
384 check(new File("/bin/true").exists());
385 check(new File("/bin/false").exists());
386 ProcessBuilder pb1 = new ProcessBuilder();
387 ProcessBuilder pb2 = new ProcessBuilder();
388 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
389 ProcessResults r;
390
391 for (final ProcessBuilder pb :
392 new ProcessBuilder[] {pb1, pb2}) {
393 pb.command("true");
394 equal(run(pb).exitValue(), True.exitValue());
395
396 pb.command("false");
397 equal(run(pb).exitValue(), False.exitValue());
398 }
399
400 if (failed != 0) throw new Error("null PATH");
401 } else if (action.equals("PATH search algorithm")) {
402 equal(System.getenv("PATH"), "dir1:dir2:");
403 check(new File("/bin/true").exists());
404 check(new File("/bin/false").exists());
405 String[] cmd = {"prog"};
406 ProcessBuilder pb1 = new ProcessBuilder(cmd);
407 ProcessBuilder pb2 = new ProcessBuilder(cmd);
408 ProcessBuilder pb3 = new ProcessBuilder(cmd);
409 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
410 pb3.environment().remove("PATH");
411
412 for (final ProcessBuilder pb :
413 new ProcessBuilder[] {pb1, pb2, pb3}) {
414 try {
415 // Not on PATH at all; directories don't exist
416 try {
417 pb.start();
418 fail("Expected IOException not thrown");
419 } catch (IOException e) {
420 String m = e.getMessage();
421 if (EnglishUnix.is() &&
422 ! matches(m, NO_SUCH_FILE_ERROR_MSG))
423 unexpected(e);
424 } catch (Throwable t) { unexpected(t); }
425
426 // Not on PATH at all; directories exist
427 new File("dir1").mkdirs();
428 new File("dir2").mkdirs();
429 try {
430 pb.start();
431 fail("Expected IOException not thrown");
432 } catch (IOException e) {
433 String m = e.getMessage();
434 if (EnglishUnix.is() &&
435 ! matches(m, NO_SUCH_FILE_ERROR_MSG))
436 unexpected(e);
437 } catch (Throwable t) { unexpected(t); }
438
439 // Can't execute a directory -- permission denied
440 // Report EACCES errno
441 new File("dir1/prog").mkdirs();
442 checkPermissionDenied(pb);
443
444 // continue searching if EACCES
445 copy("/bin/true", "dir2/prog");
446 equal(run(pb).exitValue(), True.exitValue());
447 new File("dir1/prog").delete();
448 new File("dir2/prog").delete();
449
450 new File("dir2/prog").mkdirs();
451 copy("/bin/true", "dir1/prog");
452 equal(run(pb).exitValue(), True.exitValue());
453
454 // Check empty PATH component means current directory.
455 //
456 // While we're here, let's test different kinds of
457 // Unix executables, and PATH vs explicit searching.
458 new File("dir1/prog").delete();
459 new File("dir2/prog").delete();
460 for (String[] command :
461 new String[][] {
462 new String[] {"./prog"},
463 cmd}) {
464 pb.command(command);
465 File prog = new File("./prog");
466 // "Normal" binaries
467 copy("/bin/true", "./prog");
468 equal(run(pb).exitValue(),
469 True.exitValue());
470 copy("/bin/false", "./prog");
471 equal(run(pb).exitValue(),
472 False.exitValue());
473 prog.delete();
474 // Interpreter scripts with #!
475 setFileContents(prog, "#!/bin/true\n");
476 prog.setExecutable(true);
477 equal(run(pb).exitValue(),
478 True.exitValue());
479 prog.delete();
480 setFileContents(prog, "#!/bin/false\n");
481 prog.setExecutable(true);
482 equal(run(pb).exitValue(),
483 False.exitValue());
484 // Traditional shell scripts without #!
485 setFileContents(prog, "exec /bin/true\n");
486 prog.setExecutable(true);
487 equal(run(pb).exitValue(),
488 True.exitValue());
489 prog.delete();
490 setFileContents(prog, "exec /bin/false\n");
505 equal(run(pb).exitValue(), True.exitValue());
506 dir1Prog.delete();
507 pb.command(cmd);
508
509 // Test traditional shell scripts without #!
510 setFileContents(dir1Prog, "/bin/echo \"$@\"\n");
511 pb.command(new String[] {"prog", "hello", "world"});
512 checkPermissionDenied(pb);
513 dir1Prog.setExecutable(true);
514 equal(run(pb).out(), "hello world\n");
515 equal(run(pb).exitValue(), True.exitValue());
516 dir1Prog.delete();
517 pb.command(cmd);
518
519 // If prog found on both parent and child's PATH,
520 // parent's is used.
521 new File("dir1/prog").delete();
522 new File("dir2/prog").delete();
523 new File("prog").delete();
524 new File("dir3").mkdirs();
525 copy("/bin/true", "dir1/prog");
526 copy("/bin/false", "dir3/prog");
527 pb.environment().put("PATH","dir3");
528 equal(run(pb).exitValue(), True.exitValue());
529 copy("/bin/true", "dir3/prog");
530 copy("/bin/false", "dir1/prog");
531 equal(run(pb).exitValue(), False.exitValue());
532
533 } finally {
534 // cleanup
535 new File("dir1/prog").delete();
536 new File("dir2/prog").delete();
537 new File("dir3/prog").delete();
538 new File("dir1").delete();
539 new File("dir2").delete();
540 new File("dir3").delete();
541 new File("prog").delete();
542 }
543 }
544
545 if (failed != 0) throw new Error("PATH search algorithm");
546 }
547 else throw new Error("JavaChild invocation error");
548 }
549 }
550
607 public static boolean is() { return is; }
608 private static final boolean is =
609 System.getProperty("os.name").startsWith("Windows");
610 }
611
612 static class AIX {
613 public static boolean is() { return is; }
614 private static final boolean is =
615 System.getProperty("os.name").equals("AIX");
616 }
617
618 static class Unix {
619 public static boolean is() { return is; }
620 private static final boolean is =
621 (! Windows.is() &&
622 new File("/bin/sh").exists() &&
623 new File("/bin/true").exists() &&
624 new File("/bin/false").exists());
625 }
626
627 static class UnicodeOS {
628 public static boolean is() { return is; }
629 private static final String osName = System.getProperty("os.name");
630 private static final boolean is =
631 // MacOS X would probably also qualify
632 osName.startsWith("Windows") &&
633 ! osName.startsWith("Windows 9") &&
634 ! osName.equals("Windows Me");
635 }
636
637 static class MacOSX {
638 public static boolean is() { return is; }
639 private static final String osName = System.getProperty("os.name");
640 private static final boolean is = osName.contains("OS X");
641 }
642
643 static class True {
644 public static int exitValue() { return 0; }
645 }
646
647 private static class False {
648 public static int exitValue() { return exitValue; }
649 private static final int exitValue = exitValue0();
650 private static int exitValue0() {
651 // /bin/false returns an *unspecified* non-zero number.
652 try {
653 if (! Unix.is())
654 return -1;
655 else {
656 int rc = new ProcessBuilder("/bin/false")
657 .start().waitFor();
658 check(rc != 0);
659 return rc;
660 }
661 } catch (Throwable t) { unexpected(t); return -1; }
662 }
663 }
664
665 static class EnglishUnix {
666 private static final Boolean is =
667 (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));
668
669 private static boolean isEnglish(String envvar) {
670 String val = getenv(envvar);
671 return (val == null) || val.matches("en.*") || val.matches("C");
672 }
673
674 /** Returns true if we can expect English OS error strings */
675 static boolean is() { return is; }
676 }
677
678 static class DelegatingProcess extends Process {
679 final Process p;
680
681 DelegatingProcess(Process p) {
682 this.p = p;
683 }
684
1948
1949 //----------------------------------------------------------------
1950 // PATH search algorithm on Unix
1951 //----------------------------------------------------------------
1952 try {
1953 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1954 childArgs.add("PATH search algorithm");
1955 ProcessBuilder pb = new ProcessBuilder(childArgs);
1956 pb.environment().put("PATH", "dir1:dir2:");
1957 ProcessResults r = run(pb);
1958 equal(r.out(), "");
1959 equal(r.err(), "");
1960 equal(r.exitValue(), True.exitValue());
1961 } catch (Throwable t) { unexpected(t); }
1962
1963 //----------------------------------------------------------------
1964 // Parent's, not child's PATH is used
1965 //----------------------------------------------------------------
1966 try {
1967 new File("suBdiR").mkdirs();
1968 copy("/bin/true", "suBdiR/unliKely");
1969 final ProcessBuilder pb =
1970 new ProcessBuilder(new String[]{"unliKely"});
1971 pb.environment().put("PATH", "suBdiR");
1972 THROWS(IOException.class, () -> pb.start());
1973 } catch (Throwable t) { unexpected(t);
1974 } finally {
1975 new File("suBdiR/unliKely").delete();
1976 new File("suBdiR").delete();
1977 }
1978 }
1979
1980 //----------------------------------------------------------------
1981 // Attempt to start bogus program ""
1982 //----------------------------------------------------------------
1983 try {
1984 new ProcessBuilder("").start();
1985 fail("Expected IOException not thrown");
1986 } catch (IOException e) {
1987 String m = e.getMessage();
1988 if (EnglishUnix.is() &&
|
383 equal(System.getenv("PATH"), null);
384 check(new File("/bin/true").exists());
385 check(new File("/bin/false").exists());
386 ProcessBuilder pb1 = new ProcessBuilder();
387 ProcessBuilder pb2 = new ProcessBuilder();
388 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
389 ProcessResults r;
390
391 for (final ProcessBuilder pb :
392 new ProcessBuilder[] {pb1, pb2}) {
393 pb.command("true");
394 equal(run(pb).exitValue(), True.exitValue());
395
396 pb.command("false");
397 equal(run(pb).exitValue(), False.exitValue());
398 }
399
400 if (failed != 0) throw new Error("null PATH");
401 } else if (action.equals("PATH search algorithm")) {
402 equal(System.getenv("PATH"), "dir1:dir2:");
403 check(new File(TrueExe.path()).exists());
404 check(new File(FalseExe.path()).exists());
405 String[] cmd = {"prog"};
406 ProcessBuilder pb1 = new ProcessBuilder(cmd);
407 ProcessBuilder pb2 = new ProcessBuilder(cmd);
408 ProcessBuilder pb3 = new ProcessBuilder(cmd);
409 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
410 pb3.environment().remove("PATH");
411
412 for (final ProcessBuilder pb :
413 new ProcessBuilder[] {pb1, pb2, pb3}) {
414 try {
415 // Not on PATH at all; directories don't exist
416 try {
417 pb.start();
418 fail("Expected IOException not thrown");
419 } catch (IOException e) {
420 String m = e.getMessage();
421 if (EnglishUnix.is() &&
422 ! matches(m, NO_SUCH_FILE_ERROR_MSG))
423 unexpected(e);
424 } catch (Throwable t) { unexpected(t); }
425
426 // Not on PATH at all; directories exist
427 new File("dir1").mkdirs();
428 new File("dir2").mkdirs();
429 try {
430 pb.start();
431 fail("Expected IOException not thrown");
432 } catch (IOException e) {
433 String m = e.getMessage();
434 if (EnglishUnix.is() &&
435 ! matches(m, NO_SUCH_FILE_ERROR_MSG))
436 unexpected(e);
437 } catch (Throwable t) { unexpected(t); }
438
439 // Can't execute a directory -- permission denied
440 // Report EACCES errno
441 new File("dir1/prog").mkdirs();
442 checkPermissionDenied(pb);
443
444 // continue searching if EACCES
445 copy(TrueExe.path(), "dir2/prog");
446 equal(run(pb).exitValue(), True.exitValue());
447 new File("dir1/prog").delete();
448 new File("dir2/prog").delete();
449
450 new File("dir2/prog").mkdirs();
451 copy(TrueExe.path(), "dir1/prog");
452 equal(run(pb).exitValue(), True.exitValue());
453
454 // Check empty PATH component means current directory.
455 //
456 // While we're here, let's test different kinds of
457 // Unix executables, and PATH vs explicit searching.
458 new File("dir1/prog").delete();
459 new File("dir2/prog").delete();
460 for (String[] command :
461 new String[][] {
462 new String[] {"./prog"},
463 cmd}) {
464 pb.command(command);
465 File prog = new File("./prog");
466 // "Normal" binaries
467 copy(TrueExe.path(), "./prog");
468 equal(run(pb).exitValue(),
469 True.exitValue());
470 copy(FalseExe.path(), "./prog");
471 equal(run(pb).exitValue(),
472 False.exitValue());
473 prog.delete();
474 // Interpreter scripts with #!
475 setFileContents(prog, "#!/bin/true\n");
476 prog.setExecutable(true);
477 equal(run(pb).exitValue(),
478 True.exitValue());
479 prog.delete();
480 setFileContents(prog, "#!/bin/false\n");
481 prog.setExecutable(true);
482 equal(run(pb).exitValue(),
483 False.exitValue());
484 // Traditional shell scripts without #!
485 setFileContents(prog, "exec /bin/true\n");
486 prog.setExecutable(true);
487 equal(run(pb).exitValue(),
488 True.exitValue());
489 prog.delete();
490 setFileContents(prog, "exec /bin/false\n");
505 equal(run(pb).exitValue(), True.exitValue());
506 dir1Prog.delete();
507 pb.command(cmd);
508
509 // Test traditional shell scripts without #!
510 setFileContents(dir1Prog, "/bin/echo \"$@\"\n");
511 pb.command(new String[] {"prog", "hello", "world"});
512 checkPermissionDenied(pb);
513 dir1Prog.setExecutable(true);
514 equal(run(pb).out(), "hello world\n");
515 equal(run(pb).exitValue(), True.exitValue());
516 dir1Prog.delete();
517 pb.command(cmd);
518
519 // If prog found on both parent and child's PATH,
520 // parent's is used.
521 new File("dir1/prog").delete();
522 new File("dir2/prog").delete();
523 new File("prog").delete();
524 new File("dir3").mkdirs();
525 copy(TrueExe.path(), "dir1/prog");
526 copy(FalseExe.path(), "dir3/prog");
527 pb.environment().put("PATH","dir3");
528 equal(run(pb).exitValue(), True.exitValue());
529 copy(TrueExe.path(), "dir3/prog");
530 copy(FalseExe.path(), "dir1/prog");
531 equal(run(pb).exitValue(), False.exitValue());
532
533 } finally {
534 // cleanup
535 new File("dir1/prog").delete();
536 new File("dir2/prog").delete();
537 new File("dir3/prog").delete();
538 new File("dir1").delete();
539 new File("dir2").delete();
540 new File("dir3").delete();
541 new File("prog").delete();
542 }
543 }
544
545 if (failed != 0) throw new Error("PATH search algorithm");
546 }
547 else throw new Error("JavaChild invocation error");
548 }
549 }
550
607 public static boolean is() { return is; }
608 private static final boolean is =
609 System.getProperty("os.name").startsWith("Windows");
610 }
611
612 static class AIX {
613 public static boolean is() { return is; }
614 private static final boolean is =
615 System.getProperty("os.name").equals("AIX");
616 }
617
618 static class Unix {
619 public static boolean is() { return is; }
620 private static final boolean is =
621 (! Windows.is() &&
622 new File("/bin/sh").exists() &&
623 new File("/bin/true").exists() &&
624 new File("/bin/false").exists());
625 }
626
627 static class BusyBox {
628 public static boolean is() { return is; }
629 private static final boolean is =
630 (! Windows.is() &&
631 new File("/bin/busybox").exists());
632 }
633
634 static class UnicodeOS {
635 public static boolean is() { return is; }
636 private static final String osName = System.getProperty("os.name");
637 private static final boolean is =
638 // MacOS X would probably also qualify
639 osName.startsWith("Windows") &&
640 ! osName.startsWith("Windows 9") &&
641 ! osName.equals("Windows Me");
642 }
643
644 static class MacOSX {
645 public static boolean is() { return is; }
646 private static final String osName = System.getProperty("os.name");
647 private static final boolean is = osName.contains("OS X");
648 }
649
650 static class True {
651 public static int exitValue() { return 0; }
652 }
653
654 private static class False {
655 public static int exitValue() { return exitValue; }
656 private static final int exitValue = exitValue0();
657 private static int exitValue0() {
658 // /bin/false returns an *unspecified* non-zero number.
659 try {
660 if (! Unix.is())
661 return -1;
662 else {
663 int rc = new ProcessBuilder("/bin/false")
664 .start().waitFor();
665 check(rc != 0);
666 return rc;
667 }
668 } catch (Throwable t) { unexpected(t); return -1; }
669 }
670 }
671
672 // On Alpine Linux, /bin/true and /bin/false are just links to /bin/busybox.
673 // Some tests copy /bin/true and /bin/false to files with a different filename.
674 // However, copying the busbox executable into a file with a different name
675 // won't result in the expected return codes. As workaround, we create
676 // executable files that can be copied and produce the expected return
677 // values.
678
679 private static class TrueExe {
680 public static String path() { return path; }
681 private static final String path = path0();
682 private static String path0(){
683 if (!BusyBox.is()) {
684 return "/bin/true";
685 }
686 else {
687 File trueExe = new File("true");
688 setFileContents(trueExe, "#!/bin/true\n");
689 trueExe.setExecutable(true);
690 return trueExe.getAbsolutePath();
691 }
692 }
693 }
694
695 private static class FalseExe {
696 public static String path() { return path; }
697 private static final String path = path0();
698 private static String path0(){
699 if (!BusyBox.is()) {
700 return "/bin/false";
701 }
702 else {
703 File falseExe = new File("false");
704 setFileContents(falseExe, "#!/bin/false\n");
705 falseExe.setExecutable(true);
706 return falseExe.getAbsolutePath();
707 }
708 }
709 }
710
711 static class EnglishUnix {
712 private static final Boolean is =
713 (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));
714
715 private static boolean isEnglish(String envvar) {
716 String val = getenv(envvar);
717 return (val == null) || val.matches("en.*") || val.matches("C");
718 }
719
720 /** Returns true if we can expect English OS error strings */
721 static boolean is() { return is; }
722 }
723
724 static class DelegatingProcess extends Process {
725 final Process p;
726
727 DelegatingProcess(Process p) {
728 this.p = p;
729 }
730
1994
1995 //----------------------------------------------------------------
1996 // PATH search algorithm on Unix
1997 //----------------------------------------------------------------
1998 try {
1999 List<String> childArgs = new ArrayList<String>(javaChildArgs);
2000 childArgs.add("PATH search algorithm");
2001 ProcessBuilder pb = new ProcessBuilder(childArgs);
2002 pb.environment().put("PATH", "dir1:dir2:");
2003 ProcessResults r = run(pb);
2004 equal(r.out(), "");
2005 equal(r.err(), "");
2006 equal(r.exitValue(), True.exitValue());
2007 } catch (Throwable t) { unexpected(t); }
2008
2009 //----------------------------------------------------------------
2010 // Parent's, not child's PATH is used
2011 //----------------------------------------------------------------
2012 try {
2013 new File("suBdiR").mkdirs();
2014 copy(TrueExe.path(), "suBdiR/unliKely");
2015 final ProcessBuilder pb =
2016 new ProcessBuilder(new String[]{"unliKely"});
2017 pb.environment().put("PATH", "suBdiR");
2018 THROWS(IOException.class, () -> pb.start());
2019 } catch (Throwable t) { unexpected(t);
2020 } finally {
2021 new File("suBdiR/unliKely").delete();
2022 new File("suBdiR").delete();
2023 }
2024 }
2025
2026 //----------------------------------------------------------------
2027 // Attempt to start bogus program ""
2028 //----------------------------------------------------------------
2029 try {
2030 new ProcessBuilder("").start();
2031 fail("Expected IOException not thrown");
2032 } catch (IOException e) {
2033 String m = e.getMessage();
2034 if (EnglishUnix.is() &&
|