README
Introduction
Mash provides a simple interface to interact with the file system and shell commands. It is currently similar to Node's fs Promises API but uses shell scripts. It spawns long running shell Child Processes and uses them for shell commands.
Ultimately this will become a complete JavaScript shell or ssh replacement. At the moment it is in early alpha and supports basic functionality.
Contents
[TOC]
Installation
npm install @TrevTheDev/Mash
Prerequisites
- to copy and move files:
rsync
must be installed - only tested on Ubuntu and Arch Linux, should work on many flavors of Linux - but not yet tested
- having
gio
installed is recommended
Objectives/Goals
To:
- provide a clean and consistent interface for the posix file system ( similar to node fs / shell.js )
- use shell commands
- run all shell commands in long-running Child Processes vs. a new process for each command
- provide progress information on copy and move
Lofty goals:
- a fully functional cross platform html/JavaScript desktop replacement, possibly using Electron
- a https interface that can replace ssh and be more functionally rich (auto complete, suggestions - similar to the chrome debugger scratch pad)
- a clean and consistent posix systems administration and configuration interface
- a sufficiently broad interface so that most admins will not have to resort to shell commands
- simple and easily accessible recipes for commonly asked systems admin questions
- a clean and consistent interface into common software and features.
- the above for Windows, Mac OS, Android and iOS
We are far from achieving our lofty goals. If you are inspired, please contribute by:
- help code and write some pull requests;
- providing any useful feedback; and
- logging and identifying issues.
Quick Overview
u() // current working directory object
u(['./path/to/dir', '/another/path']) // return an array of locations
u('./path/to/fileName.txt').path.name // returns 'fileName'
All the following are asynchronous and return a promise. They must be preceded by await
excluded for brevity:
u('./path/to/dir').content // read directory content returns FsObjectArray
u('/path/to/dir').u('sub/dir') // returns FSObject '/path/to/dir/sub/dir'
u('./path/to/fileordir').parent // returns containing Directory
u('./path/to/fileordir').exists // confirms existence (returns boolean)
u('./path/to/dir').addDirectory('newDir') // creates a new dir object
u('./path/to/dir').addDirectory('newDir/nextDir') // creates two new dirs [array]
u('./path/to/dir').addDirectory(['a', 'b', 'c']) // creates three new dirs [array]
u('./path/to/dir').addFile('newFile.txt', 'content') // creates a new file with content.
u('./path/to/newSymlink').linkTo('/path/to/source') // creates symlink object
u('./path/to/symlink').linkTarget // returns symlink's Target FSObject
u('./path/to/symlink').linkEndTarget // symlink's ultimate target FSObject
u('./path/to/dirOrFile').touch() // touches object
u('./path/to/dir').rename('folderx') // renames to folderx
u('./path/to/source').copyTo('/path/to/dest') // copy to destination, with progress
u('./path/to/source').moveTo('/path/to/dest') // move to destination, with progress
u('./path/to/source').delete() // rm './path/to/source'
u('./path/to/source').trash() // moves to trash using gio
server.emptyTrash() // empties trash using gio
u('./path/to/source').stat() // stat, gio, lasattr and size of source
u('./path/to/source').type // returns posix fs type
u('./path/to/source').permissions // returns permissions e.g. 775
u('./path/to/source').permissions.symbol // returns permissions e.g. 'a+rwx'
u('./path/to/source').setPermissions('a+rwx') // chmod a+rwx
u('./path/to/source').user // returns user object
u('./path/to/source').setUser('bob') // sets user to bob
u('./path/to/source').group // returns group object
u('./path/to/source').setGroup('users') // sets group to 'users'
u('./path').cloneAttrs('./path/to/clone') // clones permissions, attributes, timestamps from fsObect to ./path
u('./path/to/dir').size // directory size object [size, file count etc.]
u('./path/to/file').read() // reads content of file
u('./path/to/file').write(content) // writes content to file
u('./path/to/file').append(content) // appends content to end of file
u('path/to/dir')
.find.byName('myDoc')
.byExt('txt')
.biggerThan('100kB') // finds files
u('new/working/directory').cd() // set a new working directory
sh('some shell cmd;') // executes a shell command and returns result
server.users.currentUser // current user and group
server.pwd // gets current working directory
Example Code
import Server, {u, sh} from '@trevthedev/mash'
const server = new Server()
// think of u as being similar to a url bar except for the filesystem
const someDir = u('path/to/some/dir')
// if no path is supplied it defaults to the current working directory - pwd
const cwd = u()
console.log(`${cwd}`) // /path/to/current/working/directory
// creates a newFile in `some/dir` with content 'ABC'
const newFile = await u('some/dir').addFile('newFile.txt', 'ABC')
const statFile = await newFile.stat() // stat, gio, lsattr newFile
console.log(`${statFile.user}`) // someuser
const content = await newFile.read() // returns 'ABC'
const findIt = await u('some/dir')
.find.byName('newFile')
.byExt('txt')
.smallerThan('100kB')
console.log(`${findIt[0]}`) // some/dir/newFile.txt
// copies NewFile
const copiedFile = await newFile.copyTo('/some/other/path')
// deletes newFile
await newFile.delete()
// moves copied file to trash
await u(`/some/other/path/${copiedFile.path.base}`).trash()
// JSON of directory's content
console.log(JSON.stringify(await u().content))
// run any shell command
const cmd = await sh('echo HELLO;')
console.log(cmd.output) // HELLO\n
await Server.instance.close() // shuts down all active shell processes
For more examples please see our test scripts
Specification
Exports:
Server
<server>u
<function> seeServer.u
sh
<function> seeServer.sh
Server
Instantiation:
const server = new Server(config)
Calling u or sh will create a new Server instance using default config, if an instance doesn't already exist.
config
config
<object>
shell
<object> default shell configuration- shell config see configuration at https://github.com/TrevTheDev/shell-queue
log
<boolean> creates a log file. Default: truelogger
<object>console
<object>info
<boolean> log info messages to console. Default: falsedebug
<boolean> log debug messages to console. Default: falseerror
<boolean> log error messages to console. Default: false
file
<object>info
<string> log info messages to provided path. Default: './logs/info.log'debug
<string> log debug messages to provided path. Default: './logs/debug.log'error
<string> log error messages to provided path. Default: './logs/error.log'
server.u(paths, shell)
paths
<array of strings> | <array of paths> | <string> | <path> the path(s) to returnshell
<shellHarness> optional shell for all child commands- returns <fsObject> | <fsObjArray>
server.sh(command, doneCBPayload, doneCallback, sendToEveryShell)
shortcut to server.shell.createCommand()
command
<string> shell command to execute must terminate with a semi colon ;doneCBPayload
<anything> optional payload to pass to doneCallbackdoneCallback
<function> optional callback after command has executed, but before results returned. Whatever is returned from the doneCallback will be returned as the promise payloadsendToEveryShell
<boolean> sends command to every shell queue. Default: false- returns <commandIFace promise> evaluates to results of shell command or
doneCallback
payload
server.users
- returns: <users> see -
Users
server.config
- returns: <object> server config
server.emptyTrash()
- empties the trash using
gio trash
- returns <boolean> true if no error occurred
server.close()
- gracefully terminates any commands in the queue or shuts down any shell child processes
server.shell
- returns <shellHarness> default shell - see shell-harness for further documentation
server.executionContext
- returns <executionContext> default
executionContext
for commands
FsObject
const fsObject = u( paths, shell)
| server.u( paths, shell)
paths
<array of strings> | <paths> | <string> | <path> the path(s) to returnshell
<shellHarness> optional default shell for all child commands- returns <fsObject>| <fsObjArray>
fsObject.stat(gio, lsattr, size)
Returns a Directory
|File
|BlockDevice
|CharacterDevice
|LocalSocket
|NamedPipe
|Symlink
populated with the output ofstat
, gio
, lsattr
and find
gio
<boolean> includegio
results. Default istrue
lsattr
<boolean> includelsattr
results. Default istrue
size
<boolean> include comprehensive size results for directories. Default isfalse
Directory
|File
|BlockDevice
|CharacterDevice
|LocalSocket
|NamedPipe
|Symlink
will have the following properties populated:
permissions
<object>accessRights
<string> octal string of permissionsoctal
<sting> octal string of permissionssymbol
<string> sting of permissionsboolArray
<array> boolean array of permissionsuser
<user>group
<group>
accessRights
<string> octal string of permissionsgroup
<group>user
<user>paths
<pathContainer> see PathContainerblocksAllocated
<integer>deviceNumber
<integer>type
<FILE_TYPE_ENUMS>file
directory
symbolic link
character device
block device
local socket
named pipe
numberHardLinks
<integer>inode
<integer>majorDeviceType
<integer>minorDeviceType
<integer>timeOfBirth
<date>timeOfLastAccess
<date>timeOfLastModification
<date>timeOfLastStatusChange
<date>contentType
<string>fastContentType
<string>uri
<string>icon
<string>symbolicIcon
<string>etagValue
<string>fileId
<string>canRead
<boolean>canWrite
<boolean>canExecute
<boolean>canDelete
<boolean>canTrash
<boolean>canRename
<boolean>timeOfLastModificationMicroSec
<integer>timeOfLastAccessMicroSec
<integer>timeOfLastChangedMicroSec
<integer>mode
<integer>rdev
<integer>userReal
<string>lsattrArr
<array>size
<size> | <directorySize>DirectorySize
size
<size>diskUsage
<size>directoryCount
<integer>fileCount
<integer>
loadedGio
<boolean> true iffsObject
gio
'edloadedStat
<boolean> true iffsObject
stat
'edloadedLsattr
<boolean> true iffsObject
lsattr
'edloadedContent
<boolean> true if Directory content loadedloadedSize
<boolean> true if Directory size queriedlsattr
booleansappendOnly
compressed
noDump
extentFormat
immutable
dataJournaling
secureDeletion
noTailMerging
unDeletable
noAtimeUpdates
synchronousDirectoryUpdates
synchronousUpdates
topOfDirectoryHierarchy
hugeFile
compressionError
indexedDirectory
compressionRawAccess
compressedDirtyFile
fsObject.copyTo(destinationDirectory, copyType, confirmOverwriteCallBack)
Uses rsync
to copy files and directories
destinationDirectory
<fsObject> | <path> | <string> destination of directory of copycopyType
<CP_TYPE>:'overwrite'
'overwriteOlder'
'askBeforeOverwrite'
'doNotOverwrite'
confirmOverwriteCallBack
<function> callback that must return a <string> one of:'yes'
- overwrite individual file or directory'no'
- don't overwrite individual file or directory'all'
- overwrite all files or directories'none'
- don't overwrite any files or directories'cancel'
- cancel copyTo
- returns <progressTracker→fsObject > promise that will ultimately evaluate to destination
fsObject
ProgressTracker
Tracks copyTo
or moveTo
progress.
- Events:
progressUpdate
- members
targetBytes
<size>bytesCompleted
<size>bytesRemaining
<size>progressFileCount
<integer>targetFileCount
<integer>progressFileCount
<integer>targetDirectoryCount
<integer>progressDirectoryCount
<integer>percentageCompleted
<percentage>deltaRateOfCompletion
<rate>rateOfCompletion
<rate>deltaETC
<date-fns distance>ETC
<date-fns distance>sourceFSObject
<fsObject>destinationDirectory
<fsObject>currentSourcePath
<path>currentDestinationDirectoryPath
<path>startedAt
<date-fns formatted date>cancel()
- cancels copy
- Returns <promise→fsObject> promise that will ultimately evaluate to destination
fsObject
object
Example
const copyCmd = u('directoryOrFileToCopy').copyTo('copy/to/here')
copyCmd.on('progressUpdate', progressTracker => {
console.log(`bytesCompleted: ${progressTracker.bytesCompleted}`)
console.log(`bytesRemaining: ${progressTracker.bytesRemaining}`)
console.log(`progressFileCount: ${progressTracker.progressFileCount}`)
console.log(
`progressDirectoryCount: ${progressTracker.progressDirectoryCount}`
)
console.log(`percentageCompleted: ${progressTracker.percentageCompleted}`)
console.log(`rateOfCompletion: ${progressTracker.rateOfCompletion}`)
console.log(`ETC: ${progressTracker.ETC}`)
progressTracker.cancel() // cancels copyTo
})
await copyCmd
fsObject.moveTo(destinationDirectory, copyType, confirmOverwriteCallBack)
Uses rsync
to move files and directories. Same as copyTo, except source files and directories are deleted after move.
fsObject.rename(newName)
Uses mv
to rename files or directories
newName
<path> | <string> new name- returns <promise→fsObject> renamed file or directory
fsObject.cloneAttrs(sourceFsObject)
sourceFsObject
<fsObject> source file system object whose attributes should be copied- returns <fsObject> matches owner (
chown
), permissions (chmod
), attributes (chattr
) and timestamps (touch
)
fsObject.addDirectory(nameArr, ignoreAnyExistingDirectories)
Uses mkdir
to create new directories
nameArr
<path> | <string> | <array of paths> | <array of string> new directory name(s)ignoreAnyExistingDirectories
<boolean> whether to overwrite existing directories or files - default isfalse
- returns <promise→Directory|Array of Directories> newly created directories
nameArr
of ['A1','A2','A3] will result in a directories as follows: path+/A1/A2/A3nameArr
of ['A1'],['A2'],['A3'] will result in a directories of path+/A1, path+/A2, path+/A3nameArr
of [['A1','B1','B2'],['A2'],['A3'] will result in path+/A1/B1/B2, path+/A2, path+/A3
fsObject.addFile(name, content, overwrite)
Creates a new file
name
<path> | <string> new file namecontent
<string> content to write to fileoverwrite
<boolean> whether to overwrite existing directories or files - default isfalse
- returns <promise→file> newly created file
fsObject.find
See FindBuilder below
fsObject.linkTo(destination, overwrite)
Creates a symbolic link fsObject
to destination using ln
.
destination
<path> | <string> destination to be symlinked tooverwrite
<boolean> remove any existing file or directory. Default: false- returns <promise→symlink> newly created symlink
fsObject.linkTarget
- returns <promise→fsObject> the target of a symlink
fsObject.linkEndTarget
- returns <promise→fsObject>` the ultimate target of a symlink or chain of symlinks
fsObject.touch()
- returns <promise→fsObject>
touch's
fsObject
fsObject.delete(recursive, limitToThisDirsFileSystem, onlyIfExists)
deletes fsObject
from file system using rm
recursive
<boolean> delete recursively if directory. Default:false
limitToThisDirsFileSystem
<boolean> when removing a hierarchy recursively, skip any directory that is on a file system different from that of the correspondingdestination
argument . Default:false
onlyIfExists
<boolean> only deletes the file system object if it already exists, else returns false. Default:false
- returns <promise→boolean> true
fsObject.trash()
gio trash
fsObject
- returns <boolean>
true
if successful
fsObject.setPermissions(permissions, applyRecursively)
sets fsObject
file system permissions using chmod
permissions
<string> any permission string accepted by chmodapplyRecursively
<boolean> if directory apply permissions recursively. Default:false
- returns <promise→fsObject> changed file system permissions
fsObject.content
returns a <fsObjectArray> populated with a directories contents if it is already loaded, otherwise returns a promise that loads it first
- returns <promise | fsObjectArray> directory's contents | <Promise> resolves to directory's contents
fsObject.dir(gio, lsattr, size, recursively)
returns a <fsObjectArray> populated with a directories contents if it is already loaded, otherwise returns a promise that loads it first
gio
<boolean> gio each item in directory's contents. Default:true
lsattr
<boolean> lsattr each item in directory's contents. Default:true
size
<boolean> query each directory's size. Default:false
applyRecursively
<boolean> recursively populate all child directories. Default:false
- returns <promise→fsObjectArray> resolves to directory's contents
fsObject.setUser(user, group, applyRecursively)
changes user and/or group using chown
user
<string> | <user> new usergroup
<string> | <group> new groupapplyRecursively
<boolean> if directory changes user or group recursively. Default:false
- returns <promise→fsObject> changed file system permissions
fsObject.setGroup(group, applyRecursively)
changes group using chgrp
group
<string> | <group> new groupapplyRecursively
<boolean> if directory changes group recursively. Default:false
- returns <promise→fsObject> changed file system permissions
fsObject.read()
- returns <promise→string> of file contents using
cat
fsObject.readStream(options)
options
<object> see herereturns <readStream> of file contents
fsObject.write(content, overwrite)
writes content to a file
content
<string> string to write to fileoverwrite
<boolean> overwrite the file if it already exists. Default:false
- returns <promise→fsObject> new or overwritten file
Requires both the user who launched mash, and the shell user to have access to os.tmpdir()
fsObject.writeStream(readStream, overwrite)
Writes readable Stream to a file
readableStream
<readableStream> stream to write to fileoverwrite
<boolean> overwrite the file if it already exists. Default:false
- returns <promise→fsObject> new or overwritten file
Uses Node's File System module to write the file.
fsObject.append(content, encoding, mode)
Append data to a file, creating the file if it does not exist. Note this is not yet using the shell, but rather node's fsPromises
content
<string> string to write to fileencoding
<string> content encoding. Default:utf8
mode
<string> if not supplied, the default of0o666
is used.- returns <promise→fsObject> appended file
fsObject.readChunk(startPosition, numberOfBytes, encoding)
Reads from startPosition
for numberOfBytes
and returns it as a <string>. Note this is not yet using the shell, but rather node's read
startPosition
<integer> position in bytes to start reading fromnumberOfBytes
<integer> number of bytes to readencoding
<string> if not supplied, the default ofutf8
is used- returns <string> contents of file
fsObject.writeChunk(chunk, startPosition, encoding)
Writes chunk
from startPosition
and returns <boolean> if successful. Note this is not yet using the shell, but rather node's write
chunk
<string> content to write to filestartPosition
<integer> position in bytes to start writing fromencoding
<string> if not supplied, the default ofutf8
is used- returns <boolean> true if successful
fsObject.exists
- returns <boolean>
true
if path exists and is accessible on file system usesrealpath
fsObject.parent
- returns <fsObject> object that is the containing parent or undefined if already root
fsObject.u(path)
- path <path> | <string> relative child path
- returns <fsObject> if
fsObject
path is/home
fsObject.u('user/path')
will reference/home/user/path
fsObject.executionContext
- returns <executionContext>
executionContext
used byfsObject
fsObject.sh(command, doneCBPayload, doneCallback, sendToEveryShell)
provides access to the fsObject's referenced shell instance.
command
<string> shell command to execute must terminate with a semi colon ;doneCBPayload
<anything> optional payload to pass to doneCallbackdoneCallback
<function> optional callback after command has executed, but before results returned. Whatever is returned from thedoneCallback
will be returned as the promise payloadsendToEveryShell
<anything> sends same command to everyShellQueue
- returns <commandIFace> promise that evaluates to results of the shell command or return from
doneCallback
fsObject.paths
- returns <pathContainer> see PathContainer
fsObject.path
- returns <path> see Path
fsObject.toString()
- returns <string> path string
fsObject.toJSON()
- returns <JSON> path string if not loaded, otherwise JSON of all properties
Directory
extents FsObject
excludes: linkTo
, linkTarget
, linkEndTarget
,read
, write
, append
, readChunk
, writeChunk
, writeStream
modifies
Directory.toJSON(pathOnly, expandContent)
pathOnly
<boolean> if true returns only the path string. Default: falseexpandContent
<boolean> if true includes a directories contents in the returned JSON. It doesn'tstat
the content, only includes it if it has previously beenstat
'ed. It recursively includes sub directories. Default: true- returns <object> json of Directory
File
extents FsObject
excludes: linkTo
, linkTarget
, linkEndTarget
, addDirectory
, addFile
, dir
, cd
, content
, find
modifies:
delete(onlyIfExists = false)
setPermissions(permissions)
setUser(user, group)
setGroup(group)
Symlink
extents FsObject
BlockDevice & CharacterDevice & LocalSocket & NamedPipe
extents FsObject
excludes: linkTo
, linkTarget
, linkEndTarget
, addDirectory
, addFile
, dir
, cd
, content
, find
modifies:
delete(onlyIfExists = false)
setPermissions(permissions)
setUser(user, group)
setGroup(group)
FsObjectArray
fsObjectArray.directories
- returns <array> all the directories in the array
fsObjectArray.files
- returns <array> all the files in the array
PathContainer
requestedPath
- returns <path> path originally requested
canonisedPath
- returns <path> path returned via
realpath
- returns <path> path returned via
canonised
- returns <boolean> if path has been canonised via the shell
statPath
- returns <path> path returned after
await fsObject.stat()
- returns <path> path returned after
symlinkTargetPath
- returns <path> symlink target if symlink via
readlink
- returns <path> symlink target if symlink via
path
- returns <path>
statPath
||canonisedPath
||requestedPath
- returns <path>
canoniseRequestPath
()- returns
<promise→path>
path canonise'ed byrealpath
- returns
getSymlinkTargetPath()
- returns <promise→path> symlink target path
exists
()- returns <promise→boolean> path exists on disk
Path
root
<path> root of pathisRoot
<boolean> true if path is rootext
<string> the file's extensionname
<path> the name excluding anyext
base
<path> the name including anyext
pathString
<string> the path as a stringisAbsolute
<boolean> true if path is absolute, false if relativeparentPath
<path> path of the containing parentaddSegment
(segment)- segment <string> | <path> segment to add to path
toArray
<array> path as an arraytoString
<string> path as a stringtoJSON
<json> path as json
FindBuilder
Builds the options for the find
command. It provides the following helper functions:
byName(name)
byRegEx(regex)
note regex is prefixed with `.*/`ignoreCase()
makesbyName
andbyRegEx
case insensitivebyInode(inodeNumber)
byExt(ext)
- appends .ext | *.ext to namebyType(type)
- type isFILE_TYPE_ENUMS
isDirectory()
- directories onlyisFile()
- files onlyisSymlink()
- simlinks onlybiggerThan(size)
size isinteger
of bytes |size
object |string
representing sizesmallerThan(size)
size isinteger
of bytes |size
object |string
representing sizeisEmpty()
- only empty files or directoriesmodifiedWithin(minutes)
minutes isinteger
modifiedAtLeast(minutes)
minutes isinteger
accessedWithin(minutes)
minutes isinteger
accessedAtLeast(minutes)
minutes isinteger
metaDataModifiedWithin(minutes)
minutes isinteger
metaDataModifiedAtLeast(minutes)
minutes isinteger
byGroup(group)
group isstring
|group
byGID(gid)
gid isinteger
hasNoGroup()
byUser(user)
user isstring
|user
byUID(uid)
uid isinteger
hasNoUser()
ignoreSubdirectories()
isExecutable()
- by current userisReadable()
- by current userisWritable()
- by current user
Later options may overwrite options that proceed them.
Alternatively options can be set directly via findBuilder.options(options)
options object includes:
maxDepthToSearch
: Default:undefined
,name
: Default:undefined
,regex
: Default:undefined
,caseInsensitive
: Default:false
inodeNumber
: Default:undefined
,ext
: Default:undefined
,type
: Default:undefined
,biggerThan
: Default:undefined
,smallerThan
: Default:undefined
,onlyIfEmptyFileOrDir
: Default:false
,contentLastModifiedMinutesAgo
: Default:undefined
,lastAccessedMinutesAgo
: Default:undefined
,metaDataLastModifiedMinutesAgo
: Default:undefined
,group
: Default:undefined
,gid
: Default:undefined
,hasNoGroup
: Default:false
,user
: Default:undefined
,uid
: Default:undefined
,hasNoUser
: Default:false
,atLeastMatchPermissions
: Default:undefined
,isExecutable
: Default:false
,isReadable
: Default:false
,isWritable
: Default:false
FindBuilder
is a promise that evaluates to an fsObjectArray
of the results of the find command.
Example Code
import {u} from '@trevthedev/mash'
//the following find options make no sense but is shown for illustration purposes
const findResult = await u('some/path')
.find.byName('someName')
.byExt('.txt')
.byRegEx('some.reg.ex')
.ignoreCase()
.byInode(123)
.byType(FILE_TYPE_ENUMS.file)
.isDirectory()
.isFile()
.isSymlink()
.biggerThan('1B')
.smallerThan('200kB')
.isEmpty()
.modifiedWithin(60)
.modifiedAtLeast(60)
.accessedWithin(60)
.accessedAtLeast(60)
.metaDataModifiedWithin(60)
.metaDataModifiedAtLeast(60)
.byGroup('users')
.byGID(1001)
.hasNoGroup()
.byUser('user')
.byUID(1001)
.hasNoUser()
.ignoreSubdirectories()
.isExecutable()
.isReadable()
.isWritable()
// alternatively:
const findResult2 = await u('some/path').find.options({
atLeastMatchPermissions: undefined,
biggerThan: 1,
caseInsensitive: true,
contentLastModifiedMinutesAgo: '+60',
ext: '.txt',
gid: 1001,
group: 'users',
hasNoGroup: false,
hasNoUser: true,
inodeNumber: 123,
isExecutable: true,
isReadable: true,
isWritable: true,
lastAccessedMinutesAgo: '+60',
maxDepthToSearch: 1,
metaDataLastModifiedMinutesAgo: '+60',
name: 'someName',
onlyIfEmptyFileOrDir: true,
regex: 'some.reg.ex',
smallerThan: 200000,
type: FILE_TYPE_ENUMS.symbolicLink,
uid: 1001,
user: 'user'
})
Users
accessed via server.users
getUser
(uidOrName
)uidOrName
<string> optional uid or name to look up using id, if none is supplied the user logged into the shell is returned- returns
<promise→user>
returns user of uid or name
currentUser
<promise→user>
returns the shells current user usingid
currentGroup
<promise→group>
returns the shells current groupknownUsers
<array> array of know users previously returned via idknownGroups
<array> array of know groups previously returned via id
User
name
<string> user nameuid
<integer> uidgroups
<array> array of groups user belongs toeffectiveGroup
<group> user's effective grouptype
<string> 'user'users
<users> reference to users which links to the server and required shell
Group
name
<string> group namegid
<integer> gidtype
<string> 'group'users
<users> reference to users which links to the server and required shell
Advanced Shell Interaction Examples
Interact with shell
const cmd = server.shell.interact('echo "what is your name?" ; read name;\n')
cmd.on('data', stdout => {
if (stdout === 'what is your name?\n') {
cmd.stdin.write('Bob\necho $name\n ')
cmd.sendDoneMarker() // required to indicate that this interaction is completed
} else console.log(stdout) // `Bob\n`
})
const res = await cmd
console.log(res.output) // `what is your name?\nBob\n`
Get a root or other user shell
import {ShellHarness} from '@trevthedev/mash'
const rootShell = new ShellHarness({
user: 'root', // or other user
rootPassword: 'whatEver'
})
const res = await rootShell.createCommand('whoami;')
console.log(res.output) // 'root\n'
//or
const rootServer = new Server({
shell: {
user: 'root', // or other user
rootPassword: 'whatEver'
}
})
// now all commands will use rootServer - note only one server can run at a time
const whoami = sh('whoami;')
await rootServer.close()
Root is gained using sudo -S su
and assumes sudo
requires a password. As a hack, sending of password is delayed by config.shell.sudoWait
default is 50. If root is not be acquired increase this.
Intercept and replace output
The output from a command can be intercepted in two ways and different result substituted if required.
import Server, {sh} from '@trevthedev/mash'
const cb = (cmd, cbData) => {
console.log(cbData) // 'HIT'
return true
}
console.log(await sh('printf HELLO ;', 'HIT', cb)) // true
await Server.instance.close() // clean up
or
const callBackServer = new Server({
shell: {
doneCallback: cb
}
})
// now all commands will use doneCallback - note only one server can run at a time
console.log(await sh('printf HELLO ;', 'HIT')) // true
await Server.instance.close()
Receive data via IPC
const cmd = server.shell.interact(
'printf "{\\"ipc\\": \\"true\\"}\\n" 1>&$NODE_CHANNEL_FD ; printf HELLO ; \n'
)
cmd.on('message', data => {
console.log(data) // { ipc: "true" }
cmd.sendDoneMarker() // required to finalise cmd
})
const res = await cmd
console.log(res.output) // `HELLO`
Send data via IPC
const cmd = server.shell.interact('echo ; \n')
cmd.on('data', stdout => {
if (stdout === '\n') {
cmd.sendMessage('HELLOBOB')
cmd.stdin.write('read -r line <&3 ; printf $line ; \n')
cmd.sendDoneMarker() // required to finalise cmd
}
})
const res = await cmd
console.log(res.output) // \n"HELLOBOB"`
Node formats messages sent via IPC