2099 lines
76 KiB
Plaintext
2099 lines
76 KiB
Plaintext
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Microsoft Research Singularity
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// File: Shell.cs
|
||
|
//
|
||
|
// Note:
|
||
|
//
|
||
|
|
||
|
using DirectoryService.Utils;
|
||
|
using FileSystem.Utils;
|
||
|
using System;
|
||
|
using System.Text;
|
||
|
using System.GC;
|
||
|
using System.Runtime.Remoting;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Threading;
|
||
|
using System.Collections;
|
||
|
using System.Diagnostics;
|
||
|
|
||
|
using Microsoft.Singularity;
|
||
|
using Microsoft.SingSharp;
|
||
|
using Microsoft.Singularity.Directory;
|
||
|
using DirectoryServices.Utils;
|
||
|
using Microsoft.Singularity.V1.Services;
|
||
|
using Microsoft.Singularity.V1.Processes;
|
||
|
using Microsoft.Singularity.Io;
|
||
|
using Microsoft.Singularity.FileSystem;
|
||
|
using Microsoft.Singularity.Channels;
|
||
|
using Microsoft.Singularity.Security;
|
||
|
using Keyboard = Microsoft.Singularity.Io.Keyboard;
|
||
|
using Tty = Microsoft.Singularity.Io.Tty2006;
|
||
|
|
||
|
using Microsoft.Contracts;
|
||
|
using Microsoft.SingSharp.Reflection;
|
||
|
using Microsoft.Singularity.Applications;
|
||
|
using Microsoft.Singularity.Io;
|
||
|
using Microsoft.Singularity.Configuration;
|
||
|
|
||
|
using System.IO;
|
||
|
|
||
|
[assembly: Transform(typeof(ApplicationResourceTransform))]
|
||
|
[assembly: ApplicationPublisherAttribute("singularity.microsoft.com")]
|
||
|
|
||
|
namespace Microsoft.Singularity.Applications
|
||
|
{
|
||
|
[ConsoleCategory(HelpMessage="Shell", DefaultAction=true)]
|
||
|
internal class Parameters
|
||
|
{
|
||
|
[InputEndpoint("data")]
|
||
|
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
|
||
|
|
||
|
[OutputEndpoint("data")]
|
||
|
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
|
||
|
|
||
|
[StringArrayParameter( "filename", HelpMessage="the bucket")]
|
||
|
internal string[] args;
|
||
|
|
||
|
reflective internal Parameters();
|
||
|
|
||
|
internal int AppMain() {
|
||
|
return Shell.AppMain(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// See WaitForChildThread below
|
||
|
internal contract WaitForChildContract {
|
||
|
out message Finish();
|
||
|
state Start: one {Finish! -> Done;}
|
||
|
state Done: one {}
|
||
|
}
|
||
|
|
||
|
public enum ShellEvent : ushort
|
||
|
{
|
||
|
RunCommand = 1
|
||
|
}
|
||
|
|
||
|
internal class Dir
|
||
|
{
|
||
|
private static TRef<DirectoryServiceContract.Imp:Ready> m_epNS = null;
|
||
|
|
||
|
public static DirectoryServiceContract.Imp:Ready! GetDirectoryServiceContract()
|
||
|
{
|
||
|
if (m_epNS == null) {
|
||
|
m_epNS = new TRef<DirectoryServiceContract.Imp:Ready>(DirectoryService.NewClientEndpoint());
|
||
|
}
|
||
|
|
||
|
return m_epNS.Acquire();
|
||
|
}
|
||
|
|
||
|
public static void ReleaseDirectoryServiceContract([Claims] DirectoryServiceContract.Imp:Ready! imp)
|
||
|
{
|
||
|
m_epNS.Release(imp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class InvalidPathException : Exception
|
||
|
{
|
||
|
public InvalidPathException(string! message)
|
||
|
: base(message)
|
||
|
{}
|
||
|
}
|
||
|
|
||
|
public class Ls
|
||
|
{
|
||
|
internal static void SplitPath(string! filePath,
|
||
|
out string! dirPath,
|
||
|
out string! fileName)
|
||
|
{
|
||
|
string[] parts = filePath.Split('/');
|
||
|
if ((parts == null) || (parts.Length == 0)) {
|
||
|
// Not even a leading slash? Bah!
|
||
|
throw new InvalidPathException(String.Format("The path '{0}' is invalid.", filePath));
|
||
|
}
|
||
|
|
||
|
// The previous implementation allowed "dir hardware" to be interpreted as "dir /hardware".
|
||
|
if (parts.Length == 1) {
|
||
|
dirPath = "/";
|
||
|
fileName = (!)parts[0];
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
fileName = (!)parts[parts.Length-1];
|
||
|
|
||
|
// The directory path is the full path minus
|
||
|
// the trailing leaf part
|
||
|
// need to special case Directory Service root. Need to return "/" as dirPath
|
||
|
int len = (filePath.Length - fileName.Length - 1) > 0? filePath.Length - fileName.Length - 1 : 1;
|
||
|
dirPath = filePath.Substring(0, len);
|
||
|
}
|
||
|
|
||
|
private static int Find(string! path, string! pattern, DirectoryServiceContract.Imp:Start! ds)
|
||
|
{
|
||
|
try {
|
||
|
ErrorCode errorOut;
|
||
|
EnumerationRecords[] in ExHeap responses =
|
||
|
SdsUtils.EnumerateDirectory(ds, out errorOut);
|
||
|
if (null == responses) {
|
||
|
Console.WriteLine("Find ({0}) failed. reason:{1}",
|
||
|
"/", SdsUtils.ErrorCodeToString(errorOut));
|
||
|
return -1;
|
||
|
}
|
||
|
else {
|
||
|
for (int i = 0; i < responses.Length; i++) {
|
||
|
string displayName;
|
||
|
expose (responses[i]) {
|
||
|
string name = Bitter.ToString2(responses[i].Path);
|
||
|
if (!SdsUtils.IsMatch(name, pattern)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
displayName = name;
|
||
|
string type;
|
||
|
switch (responses[i].Type) {
|
||
|
case NodeType.Directory :
|
||
|
type = "<dir> ";
|
||
|
break;
|
||
|
case NodeType.File :
|
||
|
type = "<file>";
|
||
|
break;
|
||
|
case NodeType.IoMemory :
|
||
|
type = "<mem> ";
|
||
|
break;
|
||
|
case NodeType.ServiceProvider :
|
||
|
type = "<chan>";
|
||
|
break;
|
||
|
case NodeType.SymLink :
|
||
|
type = "<link>";
|
||
|
break;
|
||
|
case NodeType.BadNode :
|
||
|
type = "<bad> ";
|
||
|
break;
|
||
|
default:
|
||
|
type = "<none>";
|
||
|
break;
|
||
|
}
|
||
|
if (responses[i].Type == NodeType.Directory) {
|
||
|
displayName = displayName + "/";
|
||
|
}
|
||
|
else if (responses[i].Type == NodeType.SymLink) {
|
||
|
// If it's a symbolic link, show the destination.
|
||
|
displayName = displayName + "@";
|
||
|
ErrorCode linkErrorCode;
|
||
|
string linkValue;
|
||
|
if (SdsUtils.GetLinkValue(name, ds, out linkValue, out linkErrorCode)) {
|
||
|
displayName = displayName.PadRight(16) + " " + linkValue;
|
||
|
} else {
|
||
|
Console.WriteLine("Failed to query link '{0}': {1}", name, SdsUtils.ErrorCodeToString(linkErrorCode));
|
||
|
}
|
||
|
}
|
||
|
Console.WriteLine(" {0} {1}", type, displayName);
|
||
|
}
|
||
|
}
|
||
|
delete responses;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
finally {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static int ListAndSort(string! path)
|
||
|
{
|
||
|
string !file;
|
||
|
string !dir;
|
||
|
string pattern;
|
||
|
|
||
|
SplitPath(path, out dir, out file);
|
||
|
|
||
|
if ( file.IndexOf("*") != -1) {
|
||
|
pattern = file;
|
||
|
}
|
||
|
else {
|
||
|
dir = path;
|
||
|
pattern = "*";
|
||
|
}
|
||
|
DirectoryServiceContract.Imp epNS = Dir.GetDirectoryServiceContract();
|
||
|
|
||
|
DirectoryServiceContract.Imp! dirClient;
|
||
|
DirectoryServiceContract.Exp! dirServer;
|
||
|
DirectoryServiceContract.NewChannel(out dirClient, out dirServer);
|
||
|
ErrorCode errorOut;
|
||
|
|
||
|
bool ok = SdsUtils.Bind((!)dir, epNS, dirServer, out errorOut);
|
||
|
if (!ok) {
|
||
|
Console.WriteLine("Bind to '{0}' failed. reason: {1}",
|
||
|
dir, SdsUtils.ErrorCodeToString(errorOut));
|
||
|
delete dirClient;
|
||
|
Dir.ReleaseDirectoryServiceContract(epNS);
|
||
|
return -1;
|
||
|
}
|
||
|
dirClient.RecvSuccess();
|
||
|
|
||
|
Find(dir, pattern, dirClient);
|
||
|
delete dirClient;
|
||
|
Dir.ReleaseDirectoryServiceContract(epNS);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public abstract class Job {
|
||
|
private string! commandLine;
|
||
|
protected TRef<UnicodePipeContract.Imp:READY>! stdinCell;
|
||
|
public readonly int Id;
|
||
|
|
||
|
private static int IdGenerator = 0;
|
||
|
|
||
|
public string Name {
|
||
|
get { return commandLine; }
|
||
|
}
|
||
|
|
||
|
protected Job(string! command, [Claims] UnicodePipeContract.Imp:READY! stdin) {
|
||
|
this.commandLine = command;
|
||
|
this.stdinCell = new TRef<UnicodePipeContract.Imp:READY>(stdin);
|
||
|
this.Id = IdGenerator++;
|
||
|
}
|
||
|
|
||
|
public UnicodePipeContract.Imp:READY! AcquireStdin() {
|
||
|
return this.stdinCell.Acquire();
|
||
|
}
|
||
|
|
||
|
public void ReleaseStdin([Claims] UnicodePipeContract.Imp:READY! stdin) {
|
||
|
this.stdinCell.Release(stdin);
|
||
|
}
|
||
|
|
||
|
|
||
|
public string StatusName {
|
||
|
get {
|
||
|
switch (this.Status) {
|
||
|
case ProcessState.Stopped:
|
||
|
return "Stopped";
|
||
|
case ProcessState.Suspended:
|
||
|
return "Suspended";
|
||
|
case ProcessState.Active:
|
||
|
return "Active";
|
||
|
default:
|
||
|
return "Unknown";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public ProcessState Status {
|
||
|
get {
|
||
|
Process p = this.Process;
|
||
|
if (p == null) {
|
||
|
return ProcessState.Stopped;
|
||
|
}
|
||
|
return p.State;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public int ExitCode {
|
||
|
get {
|
||
|
Process p = this.Process;
|
||
|
if (p == null) {
|
||
|
return 0;
|
||
|
}
|
||
|
return p.ExitCode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public virtual void Dispose() {
|
||
|
}
|
||
|
|
||
|
public abstract Process Process { get; }
|
||
|
|
||
|
public void Stop() {
|
||
|
Process p = this.Process;
|
||
|
if (p != null) {
|
||
|
p.Stop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Suspend() {
|
||
|
Process p = this.Process;
|
||
|
if (p != null) {
|
||
|
p.Suspend(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Resume() {
|
||
|
Process p = this.Process;
|
||
|
if (p != null) {
|
||
|
p.Resume(false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class SingleProcessJob : Job {
|
||
|
|
||
|
Process process;
|
||
|
|
||
|
private static string! CommandLineString(string[]! command)
|
||
|
{
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
foreach(string s in command) {
|
||
|
sb.Append(s);
|
||
|
sb.Append(' ');
|
||
|
}
|
||
|
return sb.ToString();
|
||
|
}
|
||
|
|
||
|
public SingleProcessJob(string[]! command, Process p,
|
||
|
[Claims] UnicodePipeContract.Imp:READY! stdin)
|
||
|
: base(CommandLineString(command), stdin)
|
||
|
{
|
||
|
this.process = p;
|
||
|
}
|
||
|
|
||
|
public override Process Process {
|
||
|
get { return this.process; }
|
||
|
}
|
||
|
|
||
|
public override void Dispose() {
|
||
|
Process p = this.process;
|
||
|
if (p != null) {
|
||
|
p.Dispose(true);
|
||
|
}
|
||
|
base.Dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class PipeJob : Job {
|
||
|
Process[]! processes ;
|
||
|
|
||
|
public PipeJob(string! command,
|
||
|
[Claims] UnicodePipeContract.Imp:READY! stdin,
|
||
|
Process[]! processes)
|
||
|
: base(command, stdin)
|
||
|
{
|
||
|
this.processes = processes;
|
||
|
}
|
||
|
|
||
|
public override Process Process {
|
||
|
get {
|
||
|
int c = processes.Length;
|
||
|
if (c <= 0) return null;
|
||
|
|
||
|
return (Process)this.processes[c-1];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void Dispose() {
|
||
|
foreach (Process! p in this.processes) {
|
||
|
p.Dispose(true);
|
||
|
}
|
||
|
base.Dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class ShellControl : ITracked {
|
||
|
|
||
|
PipeMultiplexer! outputControl;
|
||
|
Hashtable! jobs; // maps ints to jobs
|
||
|
|
||
|
public ShellControl([Claims] PipeMultiplexer! outputControl) {
|
||
|
this.outputControl = outputControl;
|
||
|
this.jobs = new Hashtable();
|
||
|
}
|
||
|
|
||
|
public UnicodePipeContract.Imp:READY FreshStdout() {
|
||
|
expose(this) {
|
||
|
return this.outputControl.FreshClient();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void RemoveStoppedJobs() {
|
||
|
ArrayList toRemove = new ArrayList();
|
||
|
foreach (int i in jobs.Keys) {
|
||
|
Job job = (Job)this.jobs[i];
|
||
|
assert job != null;
|
||
|
if (job.Status == ProcessState.Stopped) {
|
||
|
job.Dispose();
|
||
|
toRemove.Add(i);
|
||
|
}
|
||
|
}
|
||
|
foreach (int i in toRemove) {
|
||
|
Console.WriteLine("[{0}] Done.", i);
|
||
|
this.jobs.Remove(i);
|
||
|
}
|
||
|
}
|
||
|
/// <summary>
|
||
|
/// Provides enumeration of non-exited jobs
|
||
|
/// </summary>
|
||
|
public IEnumerator GetEnumerator() {
|
||
|
RemoveStoppedJobs();
|
||
|
return this.jobs.Keys.GetEnumerator();
|
||
|
}
|
||
|
|
||
|
public Job this[int i] {
|
||
|
get {
|
||
|
return (Job)this.jobs[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Add(Job! job) {
|
||
|
jobs[job.Id] = job;
|
||
|
}
|
||
|
|
||
|
public void Dispose()
|
||
|
{
|
||
|
this.outputControl.Dispose();
|
||
|
}
|
||
|
|
||
|
void ITracked.Acquire() {}
|
||
|
void ITracked.Release() {}
|
||
|
void ITracked.Expose() {}
|
||
|
void ITracked.UnExpose() {}
|
||
|
}
|
||
|
|
||
|
public class Shell
|
||
|
{
|
||
|
// Exit Codes:
|
||
|
internal const int EXIT_AND_RESTART = 0x1fff;
|
||
|
internal const int EXIT_AND_SHUTDOWN = 0x1ffe;
|
||
|
internal const int EXIT_AND_WARMBOOT = 0x1ffd;
|
||
|
internal const int EXIT_AND_HALT = 0x1ffc;
|
||
|
|
||
|
private static Terminal terminal;
|
||
|
private static ParameterProcessor parameters;
|
||
|
private static ArrayList localPackages = null;
|
||
|
private static bool done = false;
|
||
|
private static bool insertMode = true;
|
||
|
private static int curpos = 0;
|
||
|
private static int totalChars = 0;
|
||
|
private static StringBuilder buildString;
|
||
|
|
||
|
private class ShellCommand
|
||
|
{
|
||
|
public string Name;
|
||
|
public string Description;
|
||
|
public CommandStart Run;
|
||
|
|
||
|
public ShellCommand(string name, string description, CommandStart run)
|
||
|
{
|
||
|
Name = name;
|
||
|
Description = description;
|
||
|
Run = run;
|
||
|
}
|
||
|
|
||
|
override public string! ToString()
|
||
|
{
|
||
|
return "[ShellCommand(" + Name + ", " + Description + "]";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////// Shell Commands.
|
||
|
//
|
||
|
private delegate int CommandStart(ShellControl! shellControl, string[]! args);
|
||
|
|
||
|
private static ShellCommand[] commands =
|
||
|
{
|
||
|
// Please insert new commands in alphabetical order!
|
||
|
//
|
||
|
new ShellCommand("bg", "Background a stopped job",
|
||
|
new CommandStart(DoBg)),
|
||
|
new ShellCommand("break", "Break into the kernel debugger",
|
||
|
new CommandStart(DoBreak)),
|
||
|
new ShellCommand("bvt", "Basic verification test",
|
||
|
new CommandStart(DoBvt)),
|
||
|
new ShellCommand("clear", "Clear screen",
|
||
|
new CommandStart(DoClear)),
|
||
|
new ShellCommand("date", "Display date and time",
|
||
|
new CommandStart(DoDate)),
|
||
|
new ShellCommand("decho", "Echo inputs to debugger",
|
||
|
new CommandStart(DoDecho)),
|
||
|
new ShellCommand("dir", "Display contents of name space",
|
||
|
new CommandStart(DoDir)),
|
||
|
new ShellCommand("echo", "Echo inputs to console",
|
||
|
new CommandStart(DoEcho)),
|
||
|
new ShellCommand("exit", "Exit shell",
|
||
|
new CommandStart(DoShutdown)),
|
||
|
new ShellCommand("fg", "Foreground a stopped or background job",
|
||
|
new CommandStart(DoFg)),
|
||
|
new ShellCommand("_gcstress", "Stress garbage collector",
|
||
|
new CommandStart(DoGcStress)),
|
||
|
new ShellCommand("help", "Display help message",
|
||
|
new CommandStart(DoHelp)),
|
||
|
new ShellCommand("jobs", "List jobs",
|
||
|
new CommandStart(DoJobs)),
|
||
|
new ShellCommand("reboot", "Reboot computer",
|
||
|
new CommandStart(DoReboot)),
|
||
|
new ShellCommand("script", "Run script file",
|
||
|
new CommandStart(DoScript)),
|
||
|
new ShellCommand("shutdown", "Shut down computer",
|
||
|
new CommandStart(DoShutdown)),
|
||
|
new ShellCommand("start", "Start process with independent I/O",
|
||
|
new CommandStart(DoStart)),
|
||
|
new ShellCommand("testscript","Test scripting engine",
|
||
|
new CommandStart(DoTestScript)),
|
||
|
new ShellCommand("warmboot", "Warm reboot computer",
|
||
|
new CommandStart(DoWarmBoot)),
|
||
|
//
|
||
|
// Please insert new commands in alphabetical order!
|
||
|
};
|
||
|
|
||
|
public static int DoBreak(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
Console.WriteLine("Breaking into kernel debugger.");
|
||
|
if (args.Length == 2 && args[1] == "-w") {
|
||
|
Breaker.Break(2);
|
||
|
}
|
||
|
else if (args.Length == 2 && args[1] == "-r") {
|
||
|
Breaker.Break(1);
|
||
|
}
|
||
|
else if (args.Length == 2 && args[1] == "-b") {
|
||
|
Breaker.Break(01);
|
||
|
}
|
||
|
else {
|
||
|
DebugStub.WriteLine("About to break into kernel debugger");
|
||
|
DebugStub.Break();
|
||
|
}
|
||
|
|
||
|
bool iflag = Processor.DisableInterrupts();
|
||
|
Processor.RestoreInterrupts(iflag);
|
||
|
Console.WriteLine("First line after break.");
|
||
|
Console.WriteLine("Second line after break.");
|
||
|
Console.WriteLine("Third line after break.");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public static int DoClear(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
terminal.GenerateAndSendEscapeSequence(Tty.EscapeCodes.CLEAR_SCREEN);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
private static void ConsoleWriteDateTime(string preamble, DateTime dt)
|
||
|
{
|
||
|
Console.WriteLine("{0}{1:d4}/{2:d2}/{3:d2} {4:d2}:{5:d2}:{6:d2}.{7:d3}",
|
||
|
preamble, dt.Year, dt.Month, dt.Day,
|
||
|
dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
|
||
|
}
|
||
|
|
||
|
private static void ConsoleWriteDateTime(string preamble, TimeSpan sp)
|
||
|
{
|
||
|
Console.WriteLine("{0}{1}", preamble, sp.ToString());
|
||
|
|
||
|
}
|
||
|
|
||
|
public static int DoDate(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
ConsoleWriteDateTime("Kernel: ", ProcessService.GetUpTime());
|
||
|
ConsoleWriteDateTime("UTC: ", DateTime.UtcNow);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
private static String Concat(String[]! args, int startIndex)
|
||
|
{
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
for (int i = startIndex; i < args.Length; ++i) {
|
||
|
sb.Append(args[i] + (i == (args.Length - 1) ? "" : " "));
|
||
|
}
|
||
|
return sb.ToString();
|
||
|
}
|
||
|
|
||
|
public static int DoDecho(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
DebugStub.WriteLine("{0}", __arglist(Concat(args, 1)));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public static int DoDir(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (args.Length == 1) {
|
||
|
ret = Ls.ListAndSort("/");
|
||
|
}
|
||
|
else {
|
||
|
for (int arg = 1; arg < args.Length; arg++) {
|
||
|
ret = Ls.ListAndSort((!)args[arg]);
|
||
|
if (ret != 0) {
|
||
|
Console.WriteLine("Returned: {0}", ret);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
public static int DoEcho(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
Console.WriteLine(Concat(args, 1));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public static int DoScript(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
int exitCode = 0;
|
||
|
long size;
|
||
|
NodeType nodeType;
|
||
|
FileContract.Imp file;
|
||
|
ErrorCode errorOut;
|
||
|
|
||
|
if (args.Length < 2) {
|
||
|
Console.WriteLine("usage: script <filename>");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
DirectoryServiceContract.Imp! rootNS = Dir.GetDirectoryServiceContract();
|
||
|
bool ok = FileUtils.GetAttributes((!)args[1], rootNS,
|
||
|
out size, out nodeType, out errorOut);
|
||
|
if (!ok) {
|
||
|
Console.WriteLine("File not found.");
|
||
|
Dir.ReleaseDirectoryServiceContract(rootNS);
|
||
|
return 1;
|
||
|
}
|
||
|
file = FileUtils.OpenFile((!)args[1], rootNS);
|
||
|
Dir.ReleaseDirectoryServiceContract(rootNS);
|
||
|
if (file == null) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
byte* opt(ExHeap[]) buf = new [ExHeap] byte[size];
|
||
|
file.SendRead(buf, 0, 0, size);
|
||
|
|
||
|
switch receive{
|
||
|
case file.AckRead(_buf, bytesRead, error):
|
||
|
buf = _buf;
|
||
|
break;
|
||
|
case file.ChannelClosed():
|
||
|
Console.WriteLine("Could not read file.");
|
||
|
delete file;
|
||
|
return 1;
|
||
|
}
|
||
|
delete file;
|
||
|
|
||
|
String script = Bitter.ToString2(buf);
|
||
|
#if DUMP_SCRIPT_CONTENTS_TO_SCREEN
|
||
|
Console.WriteLine("Script===============================");
|
||
|
Console.WriteLine(script);
|
||
|
Console.WriteLine("============================EndScript");
|
||
|
#endif
|
||
|
exitCode = ScriptEngine.Run(script, shellControl,
|
||
|
new ScriptEngine.CommandLineRunner(RunCommand));
|
||
|
delete buf;
|
||
|
return exitCode;
|
||
|
}
|
||
|
|
||
|
public static int DoStart(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
String[] commandLine = new String[args.Length - 1];
|
||
|
Array.Copy(args, 1, commandLine, 0, commandLine.Length);
|
||
|
return RunCommand(shellControl, commandLine, false);
|
||
|
}
|
||
|
|
||
|
public static int DoTestScript(ShellControl! shellControl, string[]! args){
|
||
|
String script =
|
||
|
@"
|
||
|
echo $2 #echo second argument
|
||
|
decho $2
|
||
|
echo number of arguments '=' $#
|
||
|
decho number of arguments '=' $#
|
||
|
|
||
|
echo last command exit code '=' $?
|
||
|
decho last command exit code '=' $?
|
||
|
|
||
|
type &
|
||
|
echo appear before type output
|
||
|
|
||
|
if (true) {
|
||
|
variable = false
|
||
|
if ($variable) {
|
||
|
var2 = true
|
||
|
echo broken conditional
|
||
|
decho broken conditional
|
||
|
}
|
||
|
else {
|
||
|
var2 = true
|
||
|
output1 = ""bet${variable}ween""
|
||
|
echo $output1
|
||
|
decho $output1
|
||
|
}
|
||
|
#some comments #
|
||
|
#
|
||
|
decho var2 is $var2
|
||
|
add = -2 + 4
|
||
|
echo '-2 + 4 =' $add
|
||
|
decho '-2 + 4 =' $add
|
||
|
|
||
|
mod = 6 % 5
|
||
|
echo '6 % 5 =' $mod
|
||
|
decho '6 % 5 =' $mod
|
||
|
|
||
|
div = 10 / 5
|
||
|
echo '10 / 5 =' $div
|
||
|
decho '10 / 5 =' $div
|
||
|
|
||
|
mult = 2 * 5
|
||
|
echo '2 * 5=' $mult
|
||
|
decho '2 * 5=' $mult
|
||
|
|
||
|
var1 = 5
|
||
|
var2 = 6
|
||
|
|
||
|
if($var1 < $var2){
|
||
|
echo $var1 is less than $var2
|
||
|
decho $var1 is less than $var2
|
||
|
two = 1
|
||
|
power = 10
|
||
|
echo starting loop
|
||
|
decho starting loop
|
||
|
while($power > 0){
|
||
|
two = $(two) * 2
|
||
|
power = $power - 1
|
||
|
}
|
||
|
output2 = ""2^10 = $two""
|
||
|
echo $output2
|
||
|
decho $output2
|
||
|
}
|
||
|
if ($var1 > $var2) {
|
||
|
echo $var2 is less than $var1
|
||
|
decho $var2 is less than $var1
|
||
|
}
|
||
|
else {
|
||
|
output3 = 'var1' . "" is "" . $var1
|
||
|
echo $output3
|
||
|
decho $output3
|
||
|
}
|
||
|
var = ""test""
|
||
|
if (""test"" == $var) {
|
||
|
echo string compare success
|
||
|
decho string compare success
|
||
|
}
|
||
|
exit
|
||
|
}
|
||
|
echo should not display
|
||
|
decho should not display
|
||
|
exit -1
|
||
|
";
|
||
|
|
||
|
return ScriptEngine.Run(
|
||
|
script,
|
||
|
shellControl,
|
||
|
new ScriptEngine.CommandLineRunner(RunCommand),
|
||
|
new String[] {"script", "testscript", "arg"}
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
private static void GcStressVariableSizeObjects()
|
||
|
{
|
||
|
const uint maxItemBytes = 65535;
|
||
|
const uint maxAllocatedBytes = 1000000;
|
||
|
byte dummy = 0;
|
||
|
|
||
|
Console.Write("Running variable size object test.");
|
||
|
|
||
|
DateTime start = DateTime.Now;
|
||
|
TimeSpan duration = TimeSpan.FromSeconds(10);
|
||
|
TimeSpan oneSecond = TimeSpan.FromSeconds(1);
|
||
|
|
||
|
Queue q = new Queue();
|
||
|
while (DateTime.Now - start < duration)
|
||
|
{
|
||
|
DateTime roundStart = DateTime.Now;
|
||
|
uint iterations = 0;
|
||
|
while (DateTime.Now - roundStart < oneSecond)
|
||
|
{
|
||
|
uint allocatedBytes = 0;
|
||
|
uint i = 17u * iterations;
|
||
|
while (allocatedBytes < maxAllocatedBytes)
|
||
|
{
|
||
|
uint itemBytes = 1u + (uint)((433777u * i) % maxItemBytes);
|
||
|
allocatedBytes += itemBytes;
|
||
|
i++;
|
||
|
byte [] data = new byte[itemBytes];
|
||
|
data[0] = 0xff;
|
||
|
q.Enqueue(data);
|
||
|
}
|
||
|
|
||
|
while (q.Count != 0)
|
||
|
{
|
||
|
byte [] data = (byte []!) q.Dequeue();
|
||
|
dummy ^= data[0];
|
||
|
}
|
||
|
iterations ++;
|
||
|
}
|
||
|
Console.Write(" {0}", iterations, dummy);
|
||
|
}
|
||
|
Console.Write("\n");
|
||
|
}
|
||
|
|
||
|
private static void GcStressFixedSizeObjects()
|
||
|
{
|
||
|
const int itemCount = 1024;
|
||
|
const int itemBytes = 1024;
|
||
|
|
||
|
Console.Write("Running fixed size object test.");
|
||
|
|
||
|
byte dummy = 0;
|
||
|
|
||
|
DateTime start = DateTime.Now;
|
||
|
TimeSpan duration = TimeSpan.FromSeconds(10);
|
||
|
TimeSpan oneSecond = TimeSpan.FromSeconds(1);
|
||
|
Queue q = new Queue();
|
||
|
while (DateTime.Now - start < duration)
|
||
|
{
|
||
|
DateTime roundStart = DateTime.Now;
|
||
|
int iterations = 0;
|
||
|
while (DateTime.Now - roundStart < oneSecond)
|
||
|
{
|
||
|
for (int i = 0; i < itemCount; i++)
|
||
|
{
|
||
|
byte [] data = new byte[itemBytes];
|
||
|
// Debug.Assert(data[0] == 0);
|
||
|
data[0] = 0xff;
|
||
|
q.Enqueue(data);
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < itemCount; i++)
|
||
|
{
|
||
|
byte [] data = (byte []!) q.Dequeue();
|
||
|
// Debug.Assert(data[0] == 0xff);
|
||
|
dummy ^= data[0];
|
||
|
}
|
||
|
iterations ++;
|
||
|
}
|
||
|
Console.Write(" {0}", iterations, dummy);
|
||
|
}
|
||
|
Console.Write("\n");
|
||
|
}
|
||
|
|
||
|
public static int DoGcStress(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
GcStressFixedSizeObjects();
|
||
|
GcStressVariableSizeObjects();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public static int DoHelp(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
Console.WriteLine("Singularity shell commands:");
|
||
|
|
||
|
int max = 0;
|
||
|
foreach (ShellCommand! command in commands) {
|
||
|
int length = command.Name.Length;
|
||
|
if (length > max) {
|
||
|
max = length;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (ShellCommand! command in commands) {
|
||
|
Console.Write(" {0}", command.Name);
|
||
|
for (int i = command.Name.Length; i < max; i++) {
|
||
|
Console.Write(" ");
|
||
|
}
|
||
|
Console.WriteLine(" - {0}", command.Description);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public static int DoJobs(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
foreach (int i in shellControl) {
|
||
|
Job job = shellControl[i];
|
||
|
assert job != null;
|
||
|
string status = job.StatusName;
|
||
|
Console.WriteLine("[{0}] {1,10} {2,40}", i, status, job.Name);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public static int DoFg(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
if (args == null || args.Length != 2) {
|
||
|
Console.WriteLine("Usage: fg <jobnumber>");
|
||
|
return 1;
|
||
|
}
|
||
|
int jobnum = Int32.Parse(args[1]);
|
||
|
|
||
|
Job job = shellControl[jobnum];
|
||
|
|
||
|
if (job == null) {
|
||
|
Console.WriteLine("Nonexistent job {0}", jobnum);
|
||
|
return 1;
|
||
|
}
|
||
|
if (job.Status == ProcessState.Suspended) {
|
||
|
job.Resume();
|
||
|
}
|
||
|
return WaitForJob(job);
|
||
|
}
|
||
|
|
||
|
public static int DoBg(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
if (args == null || args.Length != 2) {
|
||
|
Console.WriteLine("Usage: bg <jobnumber>");
|
||
|
return 1;
|
||
|
}
|
||
|
int jobnum = Int32.Parse(args[1]);
|
||
|
|
||
|
Job job = shellControl[jobnum];
|
||
|
|
||
|
if (job == null) {
|
||
|
Console.WriteLine("Nonexistent job {0}", jobnum);
|
||
|
return 1;
|
||
|
}
|
||
|
job.Resume();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public static int DoReboot(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
DebugStub.WriteLine("Shell rebooting.");
|
||
|
done = true;
|
||
|
return EXIT_AND_RESTART;
|
||
|
}
|
||
|
|
||
|
public static int DoShutdown(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
DebugStub.WriteLine("Shell shutting down.");
|
||
|
done = true;
|
||
|
return EXIT_AND_SHUTDOWN;
|
||
|
}
|
||
|
|
||
|
public static int DoWarmBoot(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
DebugStub.WriteLine("Shell restarting for warm boot.");
|
||
|
done = true;
|
||
|
return EXIT_AND_WARMBOOT;
|
||
|
}
|
||
|
|
||
|
// Runs each step of the BVT.
|
||
|
public static int Bvt(ShellControl! shellControl, int count)
|
||
|
{
|
||
|
Console.WriteLine("BVT BootCount={0}", count);
|
||
|
DebugStub.WriteLine("BVT BootCount={0}", __arglist(count));
|
||
|
String script;
|
||
|
|
||
|
if (count == 0) {
|
||
|
script = @"
|
||
|
msg = '[BVT 0.0] Running page table test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
|
||
|
msg = '[BVT 0.1] Running Pnp test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
if (false) {
|
||
|
pnp
|
||
|
pnp
|
||
|
pnp
|
||
|
}
|
||
|
|
||
|
msg = '[BVT 0.2] Running channel test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
channeldemo
|
||
|
channeldemo
|
||
|
channeldemo
|
||
|
|
||
|
msg = '[BVT 0.3] Running ram disk contents test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
if (false) {
|
||
|
disk
|
||
|
disk
|
||
|
disk
|
||
|
}
|
||
|
|
||
|
msg = '[BVT 0.4] Running sound driver test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
play
|
||
|
play
|
||
|
play
|
||
|
|
||
|
msg = '[BVT 0.5] Running select test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
select
|
||
|
select
|
||
|
select
|
||
|
|
||
|
msg = '[BVT 0.6] Running thread test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
threadtest
|
||
|
threadtest
|
||
|
threadtest
|
||
|
|
||
|
msg = '[BVT 0.7] Running mptxofy test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
if (false) {
|
||
|
mptxofy
|
||
|
}
|
||
|
|
||
|
msg = '[BVT 0.8] Running tasklist.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
tasklist
|
||
|
|
||
|
msg = '[BVT 0.9] Running dir test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
dir
|
||
|
dir '/init'
|
||
|
|
||
|
msg = '[BVT 0.10] Running exception test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
throw
|
||
|
throwwithlinkstack
|
||
|
|
||
|
msg = '[BVT 0.11 Running suspend/stop test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
arg1 = '(& p2 (* 9999999 print 2 delay 2 ) wall )'
|
||
|
arg2 = ' (* 5 print 1 delay 2 ) suspend p2'
|
||
|
arg3 = ' (* 5 print 1 delay 2 ) resume p2'
|
||
|
arg4 = ' (* 5 print 1 delay 2 ) stop p2'
|
||
|
arg5 = ' (* 5 print 1 delay 2 )'
|
||
|
arg = $arg1 . $arg2 . $arg3 . $arg4 . $arg5
|
||
|
kptest $arg
|
||
|
|
||
|
msg = '[BVT 0.12] Running singbench test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
singbench
|
||
|
|
||
|
msg = '[BVT 0.13] Running monitor test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
monitortest
|
||
|
|
||
|
msg = '[BVT 0.14] Running warmboot test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
warmboot
|
||
|
";
|
||
|
}
|
||
|
else if (count == 1) {
|
||
|
script = @"
|
||
|
msg = '[BVT 1.0] Running page table test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
|
||
|
msg = '[BVT 1.1] Running ram disk contents test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
if (false) {
|
||
|
disk
|
||
|
}
|
||
|
|
||
|
msg = '[BVT 1.2] Running sound driver test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
play
|
||
|
|
||
|
msg = '[BVT 1.3] Running tasklist.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
tasklist
|
||
|
|
||
|
msg = '[BVT 1.4] Running dir test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
dir
|
||
|
dir '/init'
|
||
|
|
||
|
msg = '[BVT 1.5] Running warmboot test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
warmboot
|
||
|
";
|
||
|
}
|
||
|
else {
|
||
|
script = @"
|
||
|
msg = '[BVT 2.0] Running page table test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
|
||
|
msg = '[BVT 2.1] Running shutdown test.'
|
||
|
echo $msg
|
||
|
decho $msg
|
||
|
shutdown
|
||
|
";
|
||
|
}
|
||
|
return ScriptEngine.Run(script, shellControl, new ScriptEngine.CommandLineRunner(RunCommand));
|
||
|
}
|
||
|
|
||
|
public static int DoBvt(ShellControl! shellControl, string[]! args)
|
||
|
{
|
||
|
#if false
|
||
|
// A sleezy way of avoiding confusion for new users,
|
||
|
// added very late in the day for the first RDK
|
||
|
// release.
|
||
|
|
||
|
if (!File.Exists("/init/tasklist")) {
|
||
|
Console.WriteLine("A BVT capable distro is not installed.");
|
||
|
Console.WriteLine("The system should be built with a Distro that is a superset of Distro\\BVT.proj");
|
||
|
return -1;
|
||
|
}
|
||
|
#endif // false
|
||
|
return Bvt(shellControl, (int)ProcessService.GetKernelBootCount());
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
public static void ReplaceOldCommandString(string! newCommand, int oldCommandLength)
|
||
|
{
|
||
|
// erase the old string
|
||
|
while (oldCommandLength-- > 0)
|
||
|
{
|
||
|
Console.Write("\b");
|
||
|
}
|
||
|
terminal.GenerateAndSendEscapeSequence(Tty.EscapeCodes.ERASE_FROM_CURSOR);
|
||
|
|
||
|
// and draw the new one
|
||
|
Console.Write(newCommand);
|
||
|
curpos = newCommand.Length;
|
||
|
totalChars = newCommand.Length;
|
||
|
}
|
||
|
|
||
|
|
||
|
private static ArrayList! StringToCharArrayList(string! s)
|
||
|
{
|
||
|
ArrayList al = new ArrayList();
|
||
|
|
||
|
for(int i=0; i < s.Length; i++) {
|
||
|
al.Add(s[i]);
|
||
|
}
|
||
|
return al;
|
||
|
}
|
||
|
|
||
|
private static String! CharArrayToString(ArrayList arr)
|
||
|
{
|
||
|
if (arr == null) return "";
|
||
|
return CharArrayToString(arr, 0, arr.Count);
|
||
|
}
|
||
|
|
||
|
private static String! CharArrayToString(ArrayList arr, int start, int len)
|
||
|
{
|
||
|
if (arr == null) return "";
|
||
|
|
||
|
assert start >= 0;
|
||
|
assert start+len <= arr.Count;
|
||
|
|
||
|
if (buildString == null) {
|
||
|
buildString = new StringBuilder(1024);
|
||
|
}
|
||
|
|
||
|
buildString.Length = 0;
|
||
|
for (int i=start; i < start+len; i++){
|
||
|
buildString.Append((char)(!)arr[i]);
|
||
|
}
|
||
|
return buildString.ToString();
|
||
|
}
|
||
|
|
||
|
private static bool IncrementCurpos()
|
||
|
{
|
||
|
if (++curpos > totalChars) {
|
||
|
curpos = totalChars;
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private static bool DecrementCurpos()
|
||
|
{
|
||
|
if (--curpos < 0) {
|
||
|
curpos = 0;
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
private static void DecrementTotalChars()
|
||
|
{
|
||
|
if (--totalChars < 0) {
|
||
|
totalChars = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void ShowHistory(ArrayList! a) {
|
||
|
DebugStub.WriteLine("showHistory: count={0}", __arglist(a.Count));
|
||
|
if (a.Count == 0) return;
|
||
|
|
||
|
for (int i=0; i < a.Count; i++){
|
||
|
DebugStub.WriteLine(CharArrayToString((ArrayList)a[i]));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static String! GetCommand(String prompt, ArrayList! history)
|
||
|
{
|
||
|
int key = 0;
|
||
|
bool inEscapeSequence = false;
|
||
|
ArrayList historyWorkingCopy = new ArrayList();
|
||
|
ArrayList command = new ArrayList();
|
||
|
Tty.EscapeCodes code;
|
||
|
int repeat;
|
||
|
|
||
|
// need to make a deep copy of the history now that the elements are
|
||
|
// array lists
|
||
|
for (int i=0; i< history.Count; i++){
|
||
|
historyWorkingCopy.Add(new ArrayList((ArrayList!) history[i]));
|
||
|
}
|
||
|
|
||
|
historyWorkingCopy.Add(command);
|
||
|
int historyCurrentSpot = historyWorkingCopy.Count - 1;
|
||
|
curpos = 0;
|
||
|
totalChars = 0;
|
||
|
|
||
|
Console.WriteLine();
|
||
|
Console.Write(prompt);
|
||
|
|
||
|
for (;;) {
|
||
|
key = Console.Read();
|
||
|
if (key == -1) {
|
||
|
Console.WriteLine("tty EOF reached!");
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
char c = (char)key;
|
||
|
//DebugStub.WriteLine("key={0:x}, char={1}",__arglist(key, c));
|
||
|
if ( c == (char) 0x1b) {
|
||
|
inEscapeSequence = true;
|
||
|
terminal.Reset();
|
||
|
continue;
|
||
|
}
|
||
|
if (inEscapeSequence) {
|
||
|
inEscapeSequence = terminal.ProcessEscape(c, out code, out repeat);
|
||
|
if (inEscapeSequence) continue;
|
||
|
|
||
|
if (code == Tty.EscapeCodes.UP) {
|
||
|
if (history.Count == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
if (historyCurrentSpot == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// now we can move up one row in the history
|
||
|
ArrayList al = (ArrayList!)historyWorkingCopy[historyCurrentSpot];
|
||
|
int count = al.Count;
|
||
|
historyCurrentSpot--;
|
||
|
ArrayList alNew = (ArrayList)historyWorkingCopy[historyCurrentSpot];
|
||
|
string s = CharArrayToString(alNew);
|
||
|
ReplaceOldCommandString(s, count);
|
||
|
}
|
||
|
else if (code == Tty.EscapeCodes.DOWN)
|
||
|
{
|
||
|
// if we're at the bottom of the history, don't move, but
|
||
|
// otherwise, move down one
|
||
|
if (history.Count == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
if (historyCurrentSpot == historyWorkingCopy.Count - 1) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// now we can move down one row in the history
|
||
|
int count = ((ArrayList!)historyWorkingCopy[historyCurrentSpot]).Count;
|
||
|
historyCurrentSpot++;
|
||
|
ReplaceOldCommandString(CharArrayToString((ArrayList)historyWorkingCopy[historyCurrentSpot]), count);
|
||
|
}
|
||
|
else if (code == Tty.EscapeCodes.LEFT) {
|
||
|
// adjust the cursor
|
||
|
if ( DecrementCurpos()) {
|
||
|
terminal.GenerateAndSendEscapeSequence(Tty.EscapeCodes.LEFT);
|
||
|
}
|
||
|
}
|
||
|
else if (code == Tty.EscapeCodes.RIGHT) {
|
||
|
if ( IncrementCurpos() ) {
|
||
|
terminal.GenerateAndSendEscapeSequence(Tty.EscapeCodes.RIGHT);
|
||
|
}
|
||
|
}
|
||
|
else if (code == Tty.EscapeCodes.INSERT) {
|
||
|
//toggle insert mode
|
||
|
if(insertMode == true) {
|
||
|
insertMode = false;
|
||
|
terminal.SetCursorSize('2');
|
||
|
}
|
||
|
else {
|
||
|
insertMode = true;
|
||
|
terminal.SetCursorSize('1');
|
||
|
}
|
||
|
DebugStub.WriteLine("Insert mode: {0}",__arglist(insertMode));
|
||
|
}
|
||
|
} // escape sequence
|
||
|
|
||
|
else if (c == '\b') {
|
||
|
int len = ((ArrayList!)historyWorkingCopy[historyCurrentSpot]).Count;
|
||
|
if (len > 0 && curpos > 0 ){
|
||
|
len = len -1;
|
||
|
DecrementCurpos();
|
||
|
DecrementTotalChars();
|
||
|
((ArrayList!)historyWorkingCopy[historyCurrentSpot]).RemoveAt(curpos);
|
||
|
Console.Write(c);
|
||
|
// re-write any chars after the deleted one
|
||
|
int num = len - curpos;
|
||
|
if (num > 0) {
|
||
|
string rest =
|
||
|
CharArrayToString((ArrayList)historyWorkingCopy[historyCurrentSpot],
|
||
|
curpos, num);
|
||
|
Console.Write(rest);
|
||
|
terminal.GenerateAndSendEscapeSequence(Tty.EscapeCodes.ERASE_FROM_CURSOR);
|
||
|
buildString.Length = 0;
|
||
|
for (int i=0; i < num; i++) {
|
||
|
buildString.Append('\b');
|
||
|
}
|
||
|
Console.Write(buildString.ToString());
|
||
|
}
|
||
|
else {
|
||
|
terminal.GenerateAndSendEscapeSequence(Tty.EscapeCodes.ERASE_FROM_CURSOR);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (c == '\n') {
|
||
|
Console.WriteLine();
|
||
|
|
||
|
if ( ((ArrayList!)historyWorkingCopy[historyCurrentSpot]).Count != 0)
|
||
|
{
|
||
|
return CharArrayToString((ArrayList!)historyWorkingCopy[historyCurrentSpot]);
|
||
|
}
|
||
|
Console.Write(prompt);
|
||
|
}
|
||
|
else {
|
||
|
// Only use the character if it's in the printable ASCII range
|
||
|
if ((c >= 32) && (c <= 126)) // SPACE to "~"
|
||
|
{
|
||
|
|
||
|
if (insertMode) {
|
||
|
ArrayList a = (ArrayList!)historyWorkingCopy[historyCurrentSpot];
|
||
|
string s = CharArrayToString(a, curpos, a.Count - curpos);
|
||
|
a.Insert(curpos, c);
|
||
|
Console.Write(c);
|
||
|
Console.Write(s);
|
||
|
string x = "";
|
||
|
for (int i=0; i < s.Length; i++){
|
||
|
x = x + '\b';
|
||
|
}
|
||
|
Console.Write(x);
|
||
|
totalChars++;
|
||
|
curpos++;
|
||
|
//curpos++;
|
||
|
}
|
||
|
else {
|
||
|
if (curpos < totalChars) {
|
||
|
((ArrayList!)historyWorkingCopy[historyCurrentSpot])[curpos++] = c;
|
||
|
}
|
||
|
else {
|
||
|
((ArrayList!)historyWorkingCopy[historyCurrentSpot]).Insert(curpos++, c);
|
||
|
totalChars++;
|
||
|
}
|
||
|
Console.Write(c);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
// else ignore it
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static ShellCommand FindCommand(string input)
|
||
|
{
|
||
|
foreach (ShellCommand! command in commands) {
|
||
|
if (command.Name == input) {
|
||
|
return command;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private static bool IsScript(string name, out string scriptName)
|
||
|
{
|
||
|
ErrorCode errorOut;
|
||
|
NodeType nodeType;
|
||
|
long length;
|
||
|
|
||
|
if (name == null) {
|
||
|
scriptName = null;
|
||
|
return false;
|
||
|
}
|
||
|
DirectoryServiceContract.Imp! ds = Dir.GetDirectoryServiceContract();
|
||
|
scriptName = "/init/"+name+".script";
|
||
|
bool ok = SdsUtils.GetAttributes(scriptName, ds, out length, out nodeType, out errorOut);
|
||
|
Dir.ReleaseDirectoryServiceContract(ds);
|
||
|
return ok;
|
||
|
}
|
||
|
|
||
|
public static int RunCommand(ShellControl! shellControl,
|
||
|
String[] commandLine,
|
||
|
bool isBackground
|
||
|
)
|
||
|
{
|
||
|
DirectoryServiceContract.Imp ds;
|
||
|
int exitCode = 0;
|
||
|
string path;
|
||
|
if (commandLine == null || commandLine.Length == 0) {
|
||
|
return exitCode;
|
||
|
}
|
||
|
ShellCommand command = FindCommand(commandLine[0]);
|
||
|
if (command != null) {
|
||
|
try {
|
||
|
exitCode = command.Run(shellControl, commandLine);
|
||
|
} catch (Exception ex) {
|
||
|
Console.WriteLine("Exception: " + ex.Message);
|
||
|
exitCode = -1;
|
||
|
}
|
||
|
}
|
||
|
else if (IsScript(commandLine[0], out path) ) {
|
||
|
String[]! scriptArgs = new String[commandLine.Length + 1];
|
||
|
Array.Copy(commandLine, 0, scriptArgs, 1, commandLine.Length);
|
||
|
scriptArgs[0] = "script";
|
||
|
scriptArgs[1] = path;
|
||
|
exitCode = DoScript(shellControl, scriptArgs);
|
||
|
}
|
||
|
else {
|
||
|
try {
|
||
|
UnicodePipeContract.Imp:READY childStdout = shellControl.FreshStdout();
|
||
|
if (childStdout == null) {
|
||
|
// output multiplexer dead (which means that we shouldn't use Console.WriteLine
|
||
|
DebugStub.WriteLine("-- Can't get new output pipe");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
Process process;
|
||
|
string action = null;
|
||
|
|
||
|
ds = Dir.GetDirectoryServiceContract();
|
||
|
Manifest manifest = Binder.LoadManifest(ds, commandLine[0]);
|
||
|
Dir.ReleaseDirectoryServiceContract(ds);
|
||
|
|
||
|
if (manifest != null) {
|
||
|
if (manifest.HasParameters()) {
|
||
|
//Console.WriteLine("Has parameters");
|
||
|
bool ok = parameters.ProcessParameters(commandLine,
|
||
|
manifest, out process, out action);
|
||
|
if (!ok) {
|
||
|
delete childStdout;
|
||
|
return -1;
|
||
|
}
|
||
|
else {
|
||
|
assert process != null;
|
||
|
int result = manifest.SetEndpoints(process, action, false);
|
||
|
if (result < 0) {
|
||
|
Console.WriteLine("Unable to set all endpoints for this process.");
|
||
|
delete childStdout;
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
//DebugStub.WriteLine("manifest has no parameters");
|
||
|
//Console.WriteLine("manifest has no parameters");
|
||
|
process = new Process(commandLine, null, 2);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
Console.WriteLine("'{0}' is not a command or has no manifest",
|
||
|
commandLine[0]);
|
||
|
delete childStdout;
|
||
|
|
||
|
throw new System.IO.FileNotFoundException(commandLine[0]);
|
||
|
}
|
||
|
|
||
|
UnicodePipeContract.Imp! stdinImp;
|
||
|
UnicodePipeContract.Exp! stdinExp;
|
||
|
UnicodePipeContract.NewChannel(out stdinImp, out stdinExp);
|
||
|
|
||
|
SingleProcessJob job = new SingleProcessJob(commandLine, process, stdinImp);
|
||
|
|
||
|
if (manifest != null && manifest.HasParameters() ) {
|
||
|
int stdinIndex = manifest.GetInputPipeIndex(action, "data");
|
||
|
if ( stdinIndex == -1) {
|
||
|
Console.WriteLine(" no stdin data pipe specified in manifest");
|
||
|
delete stdinExp;
|
||
|
}
|
||
|
else process.SetStartupEndpoint(stdinIndex, (Endpoint * in ExHeap) stdinExp);
|
||
|
|
||
|
int stdoutIndex = manifest.GetOutputPipeIndex(action, "data");
|
||
|
if ( stdoutIndex == -1) {
|
||
|
Console.WriteLine(" no stdout data pipe specified in manifest");
|
||
|
delete childStdout;
|
||
|
}
|
||
|
else process.SetStartupEndpoint(stdoutIndex, (Endpoint * in ExHeap) childStdout);
|
||
|
}
|
||
|
else {
|
||
|
process.SetStartupEndpoint(0, (Endpoint * in ExHeap) stdinExp);
|
||
|
process.SetStartupEndpoint(1, (Endpoint * in ExHeap) childStdout);
|
||
|
}
|
||
|
shellControl.Add(job);
|
||
|
|
||
|
process.Start();
|
||
|
if (!isBackground){
|
||
|
exitCode = WaitForJob(job);
|
||
|
if (exitCode != 0) {
|
||
|
Console.WriteLine("-- Exit code: {0}",
|
||
|
exitCode);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
catch (ProcessCreateException) {
|
||
|
Console.Write("Unsupported command: {0}", commandLine[0]);
|
||
|
}
|
||
|
catch (FileNotFoundException fe) {
|
||
|
throw fe;
|
||
|
}
|
||
|
catch (Exception e) {
|
||
|
Console.Write("Can't start {0}: Exception '{1}' caught.",
|
||
|
commandLine[0], e.Message);
|
||
|
}
|
||
|
}
|
||
|
return exitCode;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Copy and echo characters from shell stdin to job stdin.
|
||
|
/// Wait for either the job to exit gracefully, or for the user
|
||
|
/// to press control-c or control-z.
|
||
|
///
|
||
|
/// Known limitation: if the child process opens
|
||
|
/// its own keyboard channel, the shell may never see the control-c
|
||
|
/// message.
|
||
|
/// </summary>
|
||
|
private static int WaitForJob(Job! job)
|
||
|
{
|
||
|
WaitForChildContract.Imp! imp;
|
||
|
WaitForChildContract.Exp! exp;
|
||
|
WaitForChildContract.NewChannel(out imp, out exp);
|
||
|
|
||
|
PipeLookAhead cinput = ConsoleInput.AcquireInput();
|
||
|
UnicodePipeContract.Imp! childStdIn = job.AcquireStdin();
|
||
|
|
||
|
ESet<UnicodePipeContract.Imp:ACK> childStdInAck =
|
||
|
new ESet<UnicodePipeContract.Imp:ACK>();
|
||
|
ESet<UnicodePipeContract.Imp:READY> childStdInReady =
|
||
|
new ESet<UnicodePipeContract.Imp:READY>();
|
||
|
|
||
|
char[] in ExHeap exChar = new [ExHeap] char[1];
|
||
|
try {
|
||
|
WaitForChildThread.StartWaiting(job.Process,
|
||
|
new TRef<WaitForChildContract.Exp:Start>(exp));
|
||
|
|
||
|
childStdInReady.Add(childStdIn);
|
||
|
|
||
|
while (true) {
|
||
|
// invariant exChar != null && childStdInReady.Head(ep) ||
|
||
|
// exChar == null && childStdInReady.Empty
|
||
|
switch receive {
|
||
|
|
||
|
case cinput.ControlC():
|
||
|
job.Stop();
|
||
|
job.Dispose();
|
||
|
return 0;
|
||
|
|
||
|
case cinput.ControlZ() && childStdInReady.Head(ep):
|
||
|
job.Suspend();
|
||
|
job.ReleaseStdin(ep);
|
||
|
return 0;
|
||
|
|
||
|
case cinput.Char(ch) && childStdInReady.Head(ep):
|
||
|
// we have a char and childStdin is ready
|
||
|
assert exChar != null;
|
||
|
// echo character
|
||
|
Console.Write(ch);
|
||
|
exChar[0] = (char) ch;
|
||
|
ep.SendWrite(exChar,0,1);
|
||
|
exChar = null;
|
||
|
childStdInAck.Add(ep);
|
||
|
continue;
|
||
|
|
||
|
case ep.AckWrite(buffer) in childStdInAck:
|
||
|
assert exChar == null;
|
||
|
exChar = buffer;
|
||
|
childStdInReady.Add(ep);
|
||
|
continue;
|
||
|
|
||
|
case imp.Finish():
|
||
|
//Console.WriteLine("finish");
|
||
|
int exitCode = job.ExitCode;
|
||
|
job.Dispose();
|
||
|
return exitCode;
|
||
|
|
||
|
case ep.ChannelClosed() in childStdInAck:
|
||
|
delete ep;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
finally {
|
||
|
childStdInReady.Dispose();
|
||
|
childStdInAck.Dispose();
|
||
|
ConsoleInput.ReleaseInput(cinput);
|
||
|
delete imp;
|
||
|
delete exChar;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// [Hawblitzel] TODO: better ways to wait on a child process
|
||
|
private class WaitForChildThread
|
||
|
{
|
||
|
private Process! process;
|
||
|
private TRef<WaitForChildContract.Exp:Start>! expRef;
|
||
|
|
||
|
private WaitForChildThread(Process! process,
|
||
|
TRef<WaitForChildContract.Exp:Start>! expRef)
|
||
|
{
|
||
|
this.process = process;
|
||
|
this.expRef = expRef;
|
||
|
base();
|
||
|
}
|
||
|
|
||
|
public static void StartWaiting(Process process,
|
||
|
TRef<WaitForChildContract.Exp:Start>! expRef)
|
||
|
{
|
||
|
if (process == null) {
|
||
|
WaitForChildContract.Exp exp = expRef.Acquire();
|
||
|
exp.SendFinish();
|
||
|
delete exp;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
WaitForChildThread wft = new WaitForChildThread(process, expRef);
|
||
|
Thread t = new Thread(new ThreadStart(wft.Wait));
|
||
|
t.Start();
|
||
|
}
|
||
|
|
||
|
private void Wait()
|
||
|
{
|
||
|
process.Join();
|
||
|
WaitForChildContract.Exp exp = expRef.Acquire();
|
||
|
exp.SendFinish();
|
||
|
delete exp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// kinda dumb...
|
||
|
private static bool CommandLineSyntaxCheck(string! commandLine)
|
||
|
{
|
||
|
int quoteCount = 0;
|
||
|
for (int i = 0; i < commandLine.Length; i++) {
|
||
|
if (commandLine[i] == '\'') {
|
||
|
quoteCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (quoteCount % 2 == 0);
|
||
|
}
|
||
|
|
||
|
private static bool InSeparators(char c, char []! separators)
|
||
|
{
|
||
|
for (int i = 0; i < separators.Length; i++)
|
||
|
{
|
||
|
if (separators[i] == c)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private static ArrayList! Tokenize(string! input, int last,
|
||
|
char []! separators)
|
||
|
{
|
||
|
ArrayList tokens = new ArrayList();
|
||
|
|
||
|
for (int i = 0; i <= last;)
|
||
|
{
|
||
|
// Skip separators
|
||
|
while (i <= last && InSeparators(input[i], separators))
|
||
|
{
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (i > last)
|
||
|
break;
|
||
|
|
||
|
// Try to slurp word
|
||
|
int start = i;
|
||
|
while (i <= last &&
|
||
|
!InSeparators(input[i], separators) &&
|
||
|
input[i] != '\'')
|
||
|
{
|
||
|
i++;
|
||
|
}
|
||
|
if (i != start)
|
||
|
{
|
||
|
tokens.Add(input.Substring(start, i - start));
|
||
|
}
|
||
|
|
||
|
// Skip separators
|
||
|
while (i <= last && InSeparators(input[i], separators))
|
||
|
{
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (i > last)
|
||
|
break;
|
||
|
|
||
|
// Try to quoted slurp word
|
||
|
if (input[i] == '\'')
|
||
|
{
|
||
|
start = i;
|
||
|
i++;
|
||
|
while (i <= last && input[i] != '\'')
|
||
|
{
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (i <= last && input[i] == '\'')
|
||
|
{
|
||
|
tokens.Add(input.Substring(start + 1, i - start - 1));
|
||
|
i++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tokens.Add(input.Substring(start, i - start));
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
} // end for
|
||
|
|
||
|
return tokens;
|
||
|
}
|
||
|
|
||
|
private static void BreakCommandLine(string! input,
|
||
|
char []! separators,
|
||
|
out string command,
|
||
|
out string[]! commandArguments,
|
||
|
out bool isBackground)
|
||
|
{
|
||
|
isBackground = false;
|
||
|
|
||
|
if (!CommandLineSyntaxCheck(input)) {
|
||
|
command = "";
|
||
|
commandArguments = new string[0];
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Scan for trailing ampersand first
|
||
|
int last = input.Length - 1;
|
||
|
while (last > 0 && InSeparators(input[last], separators))
|
||
|
{
|
||
|
last--;
|
||
|
}
|
||
|
|
||
|
if (input[last] == '&')
|
||
|
{
|
||
|
isBackground = true;
|
||
|
last--;
|
||
|
}
|
||
|
|
||
|
ArrayList tokens = Tokenize(input, last, separators);
|
||
|
|
||
|
if (tokens.Count == 0)
|
||
|
{
|
||
|
command = "";
|
||
|
commandArguments = new string[0];
|
||
|
isBackground = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
command = (string) tokens[0];
|
||
|
commandArguments = new string [tokens.Count];
|
||
|
for (int i = 0; i < tokens.Count; i++)
|
||
|
{
|
||
|
commandArguments[i] = (string) tokens[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static int RunPipe(ShellControl! shellControl, string! input)
|
||
|
{
|
||
|
DirectoryServiceContract.Imp ds;
|
||
|
int last = input.Length - 1;
|
||
|
ArrayList tokens = Tokenize(input, last, new char[] {'|'});
|
||
|
string [] commands = new string [tokens.Count];
|
||
|
for (int i = 0; i < tokens.Count; i++)
|
||
|
{
|
||
|
commands[i] = (string) tokens[i];
|
||
|
DebugStub.WriteLine("Command : {0}",__arglist(commands[i]));
|
||
|
}
|
||
|
|
||
|
UnicodePipeContract.Imp! pipeStdin;
|
||
|
UnicodePipeContract.Exp! nonNullNextStdin;
|
||
|
|
||
|
UnicodePipeContract.NewChannel(out pipeStdin, out nonNullNextStdin);
|
||
|
|
||
|
UnicodePipeContract.Exp nextStdin = nonNullNextStdin;
|
||
|
|
||
|
Process[] processes = new Process[tokens.Count];
|
||
|
// Start up each link process.
|
||
|
|
||
|
string commandName;
|
||
|
string[]! commandArguments;
|
||
|
bool isBackground = false;
|
||
|
Process process = null;
|
||
|
Process child = null;
|
||
|
string action = null;;
|
||
|
|
||
|
for (int i = 0; i < tokens.Count; i++) {
|
||
|
if (commands[i] != null) {
|
||
|
|
||
|
UnicodePipeContract.Imp childStdout;
|
||
|
UnicodePipeContract.Exp childStdoutExp;
|
||
|
|
||
|
if (i == tokens.Count - 1) {
|
||
|
// last process, hook up to output multiplexer
|
||
|
childStdout = shellControl.FreshStdout();
|
||
|
childStdoutExp = null;
|
||
|
}
|
||
|
else {
|
||
|
UnicodePipeContract.Imp! nonNullChildImp;
|
||
|
UnicodePipeContract.Exp! nonNullChildExp;
|
||
|
UnicodePipeContract.NewChannel(out nonNullChildImp, out nonNullChildExp);
|
||
|
childStdout = nonNullChildImp;
|
||
|
childStdoutExp = nonNullChildExp;
|
||
|
}
|
||
|
|
||
|
BreakCommandLine((!)commands[i], new char[] {' '},
|
||
|
out commandName, out commandArguments,
|
||
|
out isBackground);
|
||
|
|
||
|
try {
|
||
|
//-------------------
|
||
|
ds = Dir.GetDirectoryServiceContract();
|
||
|
Manifest manifest = Binder.LoadManifest(ds, commandName);
|
||
|
Dir.ReleaseDirectoryServiceContract(ds);
|
||
|
if (manifest != null) {
|
||
|
if (manifest.HasParameters()) {
|
||
|
bool ok = parameters.ProcessParameters(commandArguments,
|
||
|
manifest, out child, out action);
|
||
|
|
||
|
if (!ok) {
|
||
|
delete childStdout;
|
||
|
delete childStdoutExp;
|
||
|
delete pipeStdin;
|
||
|
delete nextStdin;
|
||
|
return -1;
|
||
|
}
|
||
|
assert child != null;
|
||
|
manifest.SetEndpoints(child, action, false);
|
||
|
}
|
||
|
else {
|
||
|
DebugStub.WriteLine("manifest has no parameters");
|
||
|
child = new Process(commandArguments, null, 2);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
Console.WriteLine("'{0}' is not a command or has no manifest",commandArguments[0]);
|
||
|
//process = new Process(commandLine, null, 2);
|
||
|
delete childStdout;
|
||
|
delete childStdoutExp;
|
||
|
delete pipeStdin;
|
||
|
delete nextStdin;
|
||
|
return -1;
|
||
|
}
|
||
|
//--------------------
|
||
|
|
||
|
//Process child = new Process(commandArguments, null, 2);
|
||
|
process = child;
|
||
|
processes[i] = process;
|
||
|
// Console.WriteLine("Starting the Process {0}, arg count={1}.",
|
||
|
// commandArguments[0],commandArguments.Length );
|
||
|
|
||
|
int stdinIndex = manifest.GetInputPipeIndex(action, "data");
|
||
|
if ( stdinIndex == -1) {
|
||
|
Console.WriteLine(" no stdin data pipe specified in manifest");
|
||
|
delete nextStdin;
|
||
|
}
|
||
|
else child.SetStartupEndpoint(stdinIndex, (Endpoint * in ExHeap) nextStdin);
|
||
|
|
||
|
int stdoutIndex = manifest.GetOutputPipeIndex(action, "data");
|
||
|
if ( stdoutIndex == -1) {
|
||
|
Console.WriteLine(" no stdout data pipe specified in manifest");
|
||
|
delete childStdout;
|
||
|
}
|
||
|
else child.SetStartupEndpoint(stdoutIndex, (Endpoint * in ExHeap) childStdout);
|
||
|
|
||
|
child.Start();
|
||
|
}
|
||
|
catch (ProcessCreateException) {
|
||
|
Console.Write("Unsupported command: {0}", commandArguments);
|
||
|
delete nextStdin;
|
||
|
delete childStdout;
|
||
|
delete childStdoutExp;
|
||
|
delete pipeStdin;
|
||
|
return -1;
|
||
|
}
|
||
|
catch (Exception e) {
|
||
|
Console.Write("Can't start {0}: Exception '{1}' caught.", commandArguments,
|
||
|
e.Message);
|
||
|
delete nextStdin;
|
||
|
delete childStdout;
|
||
|
delete childStdoutExp;
|
||
|
delete pipeStdin;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
nextStdin = childStdoutExp;
|
||
|
}
|
||
|
}
|
||
|
assert nextStdin == null;
|
||
|
|
||
|
PipeJob job = new PipeJob(input, pipeStdin, processes);
|
||
|
shellControl.Add(job);
|
||
|
|
||
|
int exitCode;
|
||
|
if (job.Process != null && !isBackground)
|
||
|
{
|
||
|
exitCode = WaitForJob(job);
|
||
|
// DebugStub.WriteLine("pipe: wait ended");
|
||
|
|
||
|
if (exitCode != 0) {
|
||
|
Console.WriteLine("-- Exit code: {0}",
|
||
|
exitCode);
|
||
|
}
|
||
|
return exitCode;
|
||
|
}
|
||
|
else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private static bool HasPipes(string! input)
|
||
|
{
|
||
|
if (input.IndexOf('|',0) == -1 ) return false;
|
||
|
else {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static ShellControl! StartOutputPipeThread() {
|
||
|
UnicodePipeContract.Exp! newOutputExp;
|
||
|
UnicodePipeContract.Imp! newOutputImp;
|
||
|
UnicodePipeContract.NewChannel(out newOutputImp, out newOutputExp);
|
||
|
|
||
|
UnicodePipeContract.Imp stdOut = ConsoleOutput.Swap(newOutputImp);
|
||
|
if (stdOut == null) {
|
||
|
DebugStub.WriteLine("Shell not connected to a pipe output!");
|
||
|
throw new ApplicationException("Can't go on");
|
||
|
}
|
||
|
PipeMultiplexer pm = PipeMultiplexer.Start(stdOut, newOutputExp);
|
||
|
return new ShellControl(pm);
|
||
|
}
|
||
|
private static bool Compare(ArrayList! al, string! s)
|
||
|
{
|
||
|
if (al.Count != s.Length) return false;
|
||
|
for (int i=0; i < s.Length; i++) {
|
||
|
if ( (char)(!)al[i] != s[i] ) return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
internal static int AppMain(Parameters! config)
|
||
|
{
|
||
|
int exitCode = 0;
|
||
|
|
||
|
terminal = new Terminal();
|
||
|
Binder.Initialize();
|
||
|
parameters = new ParameterProcessor();
|
||
|
|
||
|
Console.WriteLine("Singularity Shell (PID={0})",
|
||
|
ProcessService.GetCurrentProcessId());
|
||
|
|
||
|
|
||
|
ShellControl! shellControl =
|
||
|
StartOutputPipeThread();
|
||
|
|
||
|
string prompt = "Singularity>";
|
||
|
string loginName = Principal.Self().GetName();
|
||
|
int i = loginName.IndexOf('+');
|
||
|
int j = loginName.IndexOf('@');
|
||
|
if (i > 0 && j > 0 && i > j) {
|
||
|
prompt = String.Format("Singularity ({0})>", loginName.Substring(j+1, i-(j+1)));
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
Console.WriteLine();
|
||
|
Console.WriteLine("Type `help' to get a list of valid commands.");
|
||
|
ArrayList history = new ArrayList();
|
||
|
|
||
|
if (config.args != null ) {
|
||
|
//String[]! scriptArgs = new String[config.args.Length - 1];
|
||
|
//Array.Copy(config.args, 1, scriptArgs, 0, scriptArgs.Length);
|
||
|
|
||
|
try {
|
||
|
exitCode = RunCommand(shellControl, config.args, false);
|
||
|
}
|
||
|
catch (FileNotFoundException) {
|
||
|
}
|
||
|
|
||
|
if (done) {
|
||
|
// we pretend we didn't connect to the keyboard.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// At startup, run the script 'startup.script'.
|
||
|
DoScript(shellControl, new string[] { "script", "/init/startup.script" });
|
||
|
|
||
|
while (!done) {
|
||
|
DebugStub.WriteLine("--- Singularity Shell Prompt ---");
|
||
|
Tracing.Log(Tracing.Warning, "--- Singularity Shell Prompt ---");
|
||
|
String inputs = GetCommand(prompt, history);
|
||
|
if (inputs.Length == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Monitoring.Log(Monitoring.Provider.Shell,
|
||
|
(ushort)ShellEvent.RunCommand,
|
||
|
inputs);
|
||
|
|
||
|
if (history.Count != 0 ) {
|
||
|
bool same = Compare ((ArrayList!)history[history.Count - 1], inputs);
|
||
|
if (!same) {
|
||
|
history.Add(StringToCharArrayList(inputs));
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
history.Add(StringToCharArrayList(inputs));
|
||
|
}
|
||
|
string commandName;
|
||
|
string[]! commandArguments;
|
||
|
bool isBackground;
|
||
|
|
||
|
//Ask for the pipe
|
||
|
if (HasPipes(inputs)) {
|
||
|
RunPipe(shellControl, inputs);
|
||
|
}
|
||
|
else {
|
||
|
BreakCommandLine(inputs, new char[] {' '},
|
||
|
out commandName, out commandArguments,
|
||
|
out isBackground);
|
||
|
try {
|
||
|
exitCode = RunCommand(shellControl, commandArguments, isBackground);
|
||
|
}
|
||
|
catch (FileNotFoundException) {
|
||
|
// No action needed, expected if user mistypes
|
||
|
// command.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
catch (Exception e) {
|
||
|
Console.WriteLine("Caught {0}", e.Message);
|
||
|
return 1;
|
||
|
}
|
||
|
finally {
|
||
|
shellControl.Dispose();
|
||
|
}
|
||
|
|
||
|
Console.WriteLine("Shell (PID={0}) Terminating w/ 0x{1:x4}.",
|
||
|
ProcessService.GetCurrentProcessId(),
|
||
|
exitCode);
|
||
|
DebugStub.WriteLine("Shell exiting w/ {0}.", __arglist(exitCode));
|
||
|
return exitCode;
|
||
|
}
|
||
|
}
|
||
|
}
|