Welcome to TiddlyWiki created by Jeremy Ruston; Copyright © 2004-2007 Jeremy Ruston, Copyright © 2007-2011 UnaMesa Association
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}
h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
.tabSelected{color:[[ColorPalette::PrimaryDark]];
background:[[ColorPalette::TertiaryPale]];
border-left:1px solid [[ColorPalette::TertiaryLight]];
border-top:1px solid [[ColorPalette::TertiaryLight]];
border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}
#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}
#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
.tiddler .defaultCommand {font-weight:bold;}
.shadow .title {color:[[ColorPalette::TertiaryDark]];}
.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}
.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}
.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}
.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}
.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}
.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
.imageLink, #displayArea .imageLink {background:transparent;}
.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}
.readOnly {background:[[ColorPalette::TertiaryPale]];}
#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity=60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}
body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}
hr {height:1px;}
a {text-decoration:none;}
dt {font-weight:bold;}
ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}
.txtOptionInput {width:11em;}
#contentWrapper .chkOptionInput {border:0;}
.externalLink {text-decoration:underline;}
.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}
.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}
/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
#mainMenu .tiddlyLinkExisting,
#mainMenu .tiddlyLinkNonExisting,
#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0 1em 1em; left:0px; top:0px;}
.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}
#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}
.wizard {padding:0.1em 1em 0 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0 0; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0;}
.wizardFooter .status {padding:0 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em;}
#messageArea {position:fixed; top:2em; right:0; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em;}
#messageArea a {text-decoration:underline;}
.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin:0;}
.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}
.tabset {padding:1em 0 0 0.5em;}
.tab {margin:0 0 0 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}
#contentWrapper {display:block;}
#splashScreen {display:none;}
#displayArea {margin:1em 17em 0 14em;}
.toolbar {text-align:right; font-size:.9em;}
.tiddler {padding:1em 1em 0;}
.missing .viewer,.missing .title {font-style:italic;}
.title {font-size:1.6em; font-weight:bold;}
.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}
.tiddler .button {padding:0.2em 0.4em;}
.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}
.footer {font-size:.9em;}
.footer li {display:inline;}
.annotation {padding:0.5em; margin:0.5em;}
* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0 0.25em; padding:0 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}
.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}
.fieldsetFix {border:0; padding:0; margin:1px 0px;}
.sparkline {line-height:1em;}
.sparktick {outline:0;}
.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}
* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin-left:3em; padding:1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none !important;}
#displayArea {margin: 1em 1em 0em;}
noscript {display:none;} /* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<!--}}}-->
To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These [[InterfaceOptions]] for customising [[TiddlyWiki]] are saved in your browser
Your username for signing your edits. Write it as a [[WikiWord]] (eg [[JoeBloggs]])
<<option txtUserName>>
<<option chkSaveBackups>> [[SaveBackups]]
<<option chkAutoSave>> [[AutoSave]]
<<option chkRegExpSearch>> [[RegExpSearch]]
<<option chkCaseSensitiveSearch>> [[CaseSensitiveSearch]]
<<option chkAnimate>> [[EnableAnimations]]
----
Also see [[AdvancedOptions]]
Run a command inside a virtual terminal
<<<
''0see [options] command args''
<<<
!DESCRIPTION
An overview of 0see can be found at [[0see Introduction|Introducing 0see]]. In this page, we discuss the various options 0see provides. Along with special usages such as running 0see under //cron//, I/O redirection, Shell Pipes and Terminal Stop/Continue (i.e, {{{CTRL-Z}}}).
0see options can be classified based on their scope as client side option {{{(C)}}} or server side option {{{(S)}}} and based on arguments as not-required {{{(-)}}} or required {{{(:)}}} or optional {{{(::)}}}. The client side options are effective only for the life of the client, ie., on detach, the effect of the option comes to an end. An example for client side option is {{{-r}}} which forces the terminal to enter raw mode. On the other hand, the server side options are effective for the life of the command being executed. An example for server side option would be {{{-1}}} which tees the standard output from command to a file.
Options which are of argument type optional {{{(::)}}} require a special mention here, these options require the argument (if any) to be specified adjacent to the option itself without any blank spaces. An example use of option {{{-j}}} has been shown below,
<<<
{{{
$ 0see -j 6805 # WRONG
...error...
$ 0see -j6805 # CORRECT
...
}}}
<<<
!!DETACH / JOIN
When running a command within 0see, the user can detach from the command using the default keyboard shortcut {{{ALT + 4}}}. This default shortcut can be disabled or changed using the option {{{-D}}}. Please note, in the case of 0seebash, the detach keyboard shortcut is disabled to provide full keyboard functionality. The two other cases, where the command runs detached, are terminal hangups and intentionally starting the command in detached mode using option {{{-d}}}.
To join the command, an ID or name needs to be specified to {{{-j}}} or {{{-N}}} respectively. To get the list of commands running under 0see with corresponding ~IDs, one should use __0seelist__. A sample output of 0seelist has been shown below,
<<<
{{{
$ 0seelist
PTY ID CMD
I/O/E(210) 16759 0see bash -c sudo tail -f /var/log/messages | grep named
I/O/E(210) 14528 0see ssh www.0throot.com
I/O/E(210) 6713 (0seebash: 48 mins)
I/O/E(210) 22700 (0seebash: 18 hours)
}}}
<<<
To make join easier for the user, a name can be specified. This has to be done while starting the command, using the option {{{-N}}}. When specifying a name, the ID argument for {{{-j}}} is optional. So one can use the options {{{-N name1 -j}}} to join a command named //name1//.
!!0seelist
0seelist is a bash script used to get the list of commands running under 0see. It reports three columns namely, ''PTY'', ''ID'' and ''CMD'' (see below).
<<<
{{{
$ 0seelist
PTY ID CMD
I/O/E(210) 16759 (zombie)
I/O/E(210) 14528 0see ssh www.0throot.com
I/O/E(210) 6713 (0seebash: 1 hours)
I/O/E(210) 22700 (0seebash: 18 hours)
}}}
<<<
''PTY'' indicates whether the standard input/output/error are on the terminal. For example, A value of {{{I/O/E(210)}}} indicates all the standard I/Os are on the terminal while a value of {{{I/-/-(0)}}} indicates only the input is on the terminal and the standard output/error could be a file or a pipe. The values in the paranthesis ie., {{{210}}} are codes used with option {{{-t}}} while joining.
''ID'' is used as an argument for option {{{-j}}}. It also indicates the process ID of the 0see server (terminal). This process will run for the life of the command being executed. In case of problems, one should kill the command being executed and never the 0see server process. Killing the 0see server process results in a dangling socket and it will be shown as {{{(zombie)}}} in the 0seelist which has to be cleaned up manually.
''CMD'' is the command that is currently running. In case, the ID corresponds to a 0see server while is no longer running, it will be shown as {{{(zombie)}}}. And, in the case of 0seebash servers, {{{(0seebash: <age>)}}} will be shown indicating the age of the bash session.
!!0seebash
0seebash is a bash script which when included in {{{.bashrc}}} will run the bash shell under 0see. It has several checks to ensure that this is done only in case of login shells. To check whether the current shell is running under 0see, one should examine the environment variable {{{_0SEE_INSIDE}}}, which will be set to 1 by 0seebash. Include 0seebash in your .bashrc by adding the statement below,
<<<
{{{
# .bashrc file
. 0seebash
}}}
<<<
On creating a login shell, 0seebash either restores the last detached 0seebash shell or creates a new one.
terminal cup mode (or alternate screen buffer) will not work in a 0seebash shell, this is because a user could start the cup mode in one terminal emulator, then detach and end the cup mode in another terminal emulator.
A word of ''@@color:red;caution@@'', you can lock yourself out of your system when using 0seebash if you are unaware of ways to restore back your .bashrc file, when facing problems.
!!OPTIONS
Several options are listed below, they have been discussed in detail in [[0see Options|0see Options]] page.
| !Option | !Scope | !Argument | !Default Value | !Without Option | !Summary |
| {{{-o}}} | client | optional | on | Refer [[options|0see Options]] page |manage terminal output processing |
| {{{-t}}} | client | optional | 210 | Refer [[options|0see Options]] page |enables pty on input/output/error |
| {{{-h}}} | client | optional | /etc/passwd | Refer [[options|0see Options]] page |specify user's home directory |
| {{{-r}}} | client | none | N/A | Refer [[options|0see Options]] page |force raw terminal |
| {{{-n}}} | client | none | N/A | Refer [[options|0see Options]] page |no change to current terminal |
| {{{-1}}} | server | required | N/A | disabled |tee output to file in argument |
| {{{-2}}} | server | required | N/A | disabled |tee error to file in argument |
| {{{-A}}} | server | none | N/A | overwrite |tee to file in append mode |
| {{{-a}}} | server | none | N/A | disabled |enable absolute mode |
| {{{-D}}} | server | optional | disable shortcut | Alt+4 |change detach shortcut to Alt+<arg> |
| {{{-d}}} | client | none | N/A | attached |run in detached mode |
| {{{-p}}} | client | optional | 10 seconds | probe disabled |probe terminal inactivity |
| {{{-N}}} | client | required | N/A | N/A |assign name to 0see terminal |
| {{{-j}}} | N/A | optional | N/A | N/A |join terminal and process |
!!I/O REDIRECTION and PIPES
Redirection and Pipes in 0see are explained in the context of bash shell. The behavior may vary in other shells. When 0see is run without any redirection, the executed command is started with both standard output and error going to the terminal. But on redirection, standard output and error for the command will instead go to the specified file (using an intermediary unnamed pipe). This default behavior can however be modified by forcing a terminal to be used for standard output and error even in case of redirection (refer to option {{{-t}}}, [[0see options|0see Options]]).
0see behaves the same way for Shell pipes as in the case of redirection. But, forcing the standard output to be on terminal (using {{{-t}}}) can result in undefined behavior. In the example below, we show the difference in output of {{{ls}}} command depending on whether the standard output is a terminal or not. Even though we pipe the {{{ls}}} output to {{{tee}}} command, we are providing a terminal to {{{ls}}} for standard output.
<<<
{{{
# standard output is a terminal
$ ls -d / /tmp /dev
/ /dev /tmp
# standard output is not a terminal
$ ls -d / /tmp /dev | tee /dev/null
/
/dev
/tmp
# standard output is forced to be on a terminal (notice the -t)
$ 0see -t ls -d / /tmp /dev | tee /dev/null
/ /dev /tmp
# Side note, this is just an example, same can be achieved using -C option in 'ls'
}}}
<<<
0see is meant to protect the running process from input hangup and errors, so __Input Redirection__ will not work. When 0see receives an ~End-Of-File (EOF) on read, it simply detaches from the input and the running process will not receive any EOF.
!!SIGTSTP
A controlling terminal can stop the execution of a process by sending a SIGTSTP signal on receipt of the key stroke //~Ctrl-Z//. This stopped process can later be resumed by sending the SIGCONT signal. When a command is run under 0see, on pressing ~Ctrl-Z, both the command and 0see client are stopped. It is expected that the user sends SIGCONT to the 0see client which resumes the command in turn. A simple demostration is shown below,
<<<
{{{
$ 0see sleep 30
^Z
[1]+ Stopped 0see sleep 30
# Note that, 0see client and 'sleep' command are in stopped state while 0see server is running.
$ ps -U seetest -ly
S UID PID PPID C PRI NI RSS SZ WCHAN TTY TIME CMD
R 502 25035 25030 0 80 0 1600 1274 - pts/4 00:00:00 bash
T 502 27441 25035 0 80 0 624 497 signal pts/4 00:00:00 0see
S 502 27442 27441 0 80 0 1164 678 poll_s pts/5 00:00:00 0see: terminal
T 502 27443 27442 0 80 0 464 981 signal pts/5 00:00:00 sleep
R 502 27464 25035 1 80 0 932 1167 - pts/4 00:00:00 ps
# Continue the process.
$ fg
...
}}}
<<<
!!TERMINAL PROBES
0seebash running on a remote machine will not know, without probing, whether the user has lost connection, so that on a reconnect the same session can be restored. A terminal probe in 0see is implemented in the form of a device query which is sent periodically for which a response is expected in a certain amount of time. On failure to receive a response, 0see assumes a terminal hangup (similar to SIGHUP) and detaches the command from the hung terminal. On reconnect, the user can join the terminal to the command.
Performing periodic device query should not interfere with the regular operations on the terminal and it should be transparent to the user. Although, depending on the implementation of the terminal emulator, scroll area might get reset to current cursor position on receiving a device query. Terminal probes can be disabled or the interval between probes can be altered using the option {{{-p}}} (refer [[0see options|0see Options]]). To be able to scroll without interruption, one can disable the //scroll on output// feature on the terminal emulator.
|!Terminal Emulator|!|!Disable Scroll on Output|
| xterm | Add option |{{{-si -sk}}} |
| gnome-terminal | Uncheck |{{{Edit > Profile Preferences > Scrolling > Scroll on output}}} |
A positive side effect of using terminal probes is that the connection does not remain idle for a time greater than the interval between probes. This ensures that, on networks that don't allow long idles connections, the connection stays established and does not get disconnected.
!!KNOWN ISSUES
0see creates a pseudo terminal for the user before executing the command. On Linux, assigning permissions to this pseudo terminal can fail which will result in 0see reporting //Server Error//. This problem is due to a failure in call to grantpt(). grantpt() uses devpts if it exists and the fall back is to use the binary /usr/libexec/pt_chown. If devpts does not exist and /usr/libexec/pt_chown does not have sufficient execute permissions then this error can occur. Do note, even if devpts is mounted, it should have mount options gid=5,mode=620 set. (gid=5, indicates group tty). Without these options, grantpt doesn't work. so, an entry for fstab should look like the following,
<<<
{{{
devpts /dev/pts devpts gid=5,mode=620 0 0
}}}
<<<
In here, you will find a detailed description of all the command line options in 0see.
!!-o (OPOST)
This option enables/disables the processing of the output terminal sequences such as changing {{{\n}}} to {{{\r\n}}}. This is essential for operations which involves output back to the terminal (such as less/tee) since 0see sets the current terminal in raw mode. By default, this option is enabled when standard output is not a terminal and remains disabled otherwise.
| !Argument | !Outcome |
| <no argument> |always enable terminal output processing |
| off |always disable terminal output processing |
| <anything else> |default behavior as explained above |
!!-t (Force PTY)
By default, 0see runs the specified command with the input/output/error on terminal if it has been started likewise. So, if 0see is started with standard output redirected to a file, the executed command will also be started with standard output being a non-terminal and standard input/error remaining on the terminal. This default behavior, however, can be changed using this option. The possible arguments have been listed below,
| !Argument | !Outcome |
| <no argument> |enable input/output/error on the terminal |
| 0 |enable only input on the terminal |
| 1 |enable only output on the terminal |
| 2 |enable only error on the terminal |
| 10 |enable only output/input on the terminal |
| 20 |enable only error/input on the terminal |
| 21 |enable only error/output on the terminal |
| 210 |enable input/output/error on the terminal |
!!-h (Home)
0see uses the home directory to create a directory structure for temporarily storing socket files. The directory structure {{{HOME/.0rt/0see}}} will be created automatically on the first run. If environment variable HOME is set, the same will be used as the home directory. Otherwise, the home directory specified in {{{/etc/passwd}}} file will be used. This option {{{-h}}} can be used to specify a different home directory and override the default behavior.
| !Argument | !Outcome |
| <no argument> |Home directory specified in {{{/etc/passwd}}}|
| <directory-path> |Home directory is set to <directory-path>|
!!-r and -n (Raw mode)
On running 0see, the 0see client modifies the current terminal settings and enables raw mode if the standard output is not a pipe. When using shell pipes to pass on the output as input to the next command, the next command in the pipe might need to change the terminal settings. In such a case, when multiple commands change the terminal settings, the result is an undefined behavior. The options {{{-r}}} and {{{-n}}} can be used to enable and disable changes to terminal settings respectively. While the option {{{-r}}} sets the current terminal in raw mode, the option {{{-n}}} informs 0see to leave the terminal alone and not change any of its settings.
!!-1, -2 and -A (Tee)
These options are used to enable writing a copy of the standard output and/or the standard error to the specified file. By enabling these options the corresponding standard output or error for the running command will not be a terminal. The option {{{-A}}} should be used along with options {{{-1}}} and {{{-2}}}, if the target file should be opened in __append__ mode. By default the output file will be truncated if append mode is not enabled. While the option {{{-1}}} is used to specify the file path for standard output, the option {{{-2}}} is used to specify the file path for standard error. By specifying {{{-}}} (hyphen), as the file path, one can ask 0see to use the same target file as mentioned for the other option, as shown in the example below,
<<<
{{{
$ 0see -1 /tmp/ls.out -2- ls -d / /tmp /notfound
/
/tmp
ls: cannot access /notfound: No such file or directory
$ cat /tmp/ls.out
/
/tmp
ls: cannot access /notfound: No such file or directory
}}}
<<<
As can be seen above, by specifying {{{-2-}}}, the file path for teeing standard error is the same as the one mentioned for {{{-1}}} ie., {{{/tmp/ls.out}}}
!!-a (absolute mode)
Absolute mode is a terminal setting in 0see which refreshes the screen instead of using terminal sequences to perform the in-place updates in the terminal emulator. Do note, the full refresh of the screen is done only for certain terminal updates. When in absolute mode, scrolling on the terminal emulator will not be available and it has to be performed by the running command. Currently, there might not exist a usecase (in 0see) where this option is required.
!!-d and -D (Detach)
Any command running in the foreground can be sent to the background by simply detaching 0see from the current terminal. The default key, {{{ALT+4}}} can be used to detach from a running command. Although, when the option {{{-d}}} is used, the command is started in detached state and the control returns to the user immediately.
<<<
{{{
$ 0see -d ping www.0throot.com
$ 0seelist
PTY ID CMD
I/O/E(210) 1407 0see -d ping www.0throot.com
I/O/E(210) 32690 (0seebash: 4 mins)
}}}
<<<
If the default key, {{{ALT+4}}}, is undesirable, it can be disabled or an alternative key combination can be provided using the option {{{-D}}}. The accepted arguments for the option have been listed below,
|!Argument|!Outcome|
| <no argument> |default detach key stroke {{{ALT+4}}} is disabled |
| <key> |sets the detach key stroke to {{{ALT+<key>}}}|
!!-p (Terminal probe)
This option enables terminal probes to be sent to the terminal emulator (by default, every 10 seconds). If a response from the terminal emulator is not received, 0see automatically detaches and leaves the running command in the background. One can specify an integer argument to this option to change the interval between two successive probes. By default, ie., without this option, terminal probes are disabled.
|!Argument|!Outcome|
| <no argument> |Terminal probes are enabled and sent every 10 seconds|
| <integer> |Terminal probes are enabled and sent every <integer> seconds|
Terminal probes are primarily used in 0seebash (refer to [[0see Manual|0see Manual]] for details).
!!-N (Naming) and -j (Join)
When a command under 0see is running in detached state, it is expected that the user runs {{{0seelist}}} to obtain an ID to join the running command to the current terminal. But since names are easier to remember, the {{{-N}}} option can be used to specify the name that will be used for join after a detach. A simple example has been shown below,
<<<
{{{
$ 0see -N ping -d ping www.0throot.com
$ 0seelist
PTY ID CMD
I/O/E(210) 12019 0see -N ping -d ping www.0throot.com
I/O/E(210) 10425 (0seebash: 6 mins)
# Join using the ID
$ 0see -j12019 # NOTE: There is no space between -j and 12019
...
OR
# Join using a NAME
$ 0see -N ping -j
...
}}}
<<<
To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
0see is an utility to run any command inside a virtual terminal to protect it from terminal hangups. It also provide a bash script to protect bash sessions from terminal hangup. This can be very useful when working remotely.
!Overview
If you are aware of popular admin tools such as //screen// and //nohup//, 0see falls somewhere right in the middle bringing in a set of benefits of its own. When you run a command within 0see, what happens is, a 0see server is spawned which creates a pseudo terminal and runs the command. This way, the 0see server handles any terminal hangups (ie., SIGHUP) and provides a way to re-establish the input/output of command to any new terminal.
<html><img src="imgs/0see-overview.png"></html>
The above illustration outlines the most common usage where 0see is invoked within a shell (such as //bash//). As you can see from the above diagram, the 0see client and 0see server communicate via a local socket which can be used to re-establish input and output connections to the command.
!Installation
0see is available in the form of a shell installer. A latest version of 0see can be downloaded from the links below,
* [[0see-0.15.i386.shin|http://www.0throot.com/download/i386/0see-0.15.i386.shin]]
* [[0see-0.15.x86_64.shin|http://www.0throot.com/download/x86_64/0see-0.15.x86_64.shin]]
To install, run {{{bash 0see-0.15.i386.shin }}} as superuser (ie., root). An optional install root can be specified.
!Basic Usage
In this section, we will see examples on how to use 0see in its most simplest form. There are several options that can be used with 0see, they are discussed in detail in [[0see Manual|0see Manual]]. We start with mentioning about the 0seebash script which can be used to protect the bash session from terminal hangups.
!!Example 1 (0seebash)
To protect a bash login shell from terminal hangups, a script 0seebash is available which automatically starts bash under 0see and restores the recently hungup bash session on login. This is most likely the common/preferred use of 0see but it should be used after __understanding the consequences__ (details [[here|0see Manual]]). To enable 0seebash, do the following,
<<<
{{{
# Add the line below to .bashrc file
. 0seebash
}}}
<<<
0seebash has several checks in place so that you don't get locked out of your system. But, it is better to take precautionary measures such as not using 0seebash on a machine where you don't have an alternate account.
!!Example 2 (Simplest)
Let us assume you have a test which can potentially run for a few hours and it should not be interrupted. In such a case, you can run your test under 0see as follows,
<<<
{{{
$ 0see -N mytest ./test_app test_args
...
}}}
<<<
The above quote shows how to run the command {{{./test_app test_args}}}. If you lose your remote connection or accidentally close the terminal emulator, you can login to shell again and restore your test as follows,
<<<
{{{
$ 0see -N mytest -j
...
}}}
<<<
The {{{-j}}} option here informs 0see to join the named 0see terminal //mytest//.
!!Example 3 (Daemonize)
Using 0see, one can run any command as a system daemon without being attached to a login shell. The example below shows how to run ping command as a daemon.
<<<
{{{
$ 0see -d ping 192.168.10.1
$
}}}
<<<
The option {{{-d}}} starts the command in detached mode. To join the ping command, an ID is required which can be obtained from running {{{0seelist}}} as shown below.
<<<
{{{
$ 0seelist
PTY ID CMD
I/O/E(210) 25354 0see -d ping 192.168.10.1
I/O/E(210) 25259 (0seebash: 2 mins)
$ 0see -j25354
...
}}}
<<<
The ID is in fact the process ID of 0see server (terminal) that spawned the original command.
<<<
{{{
$ ps -ely | grep 25354
S 502 25354 1 0 80 0 696 543 poll_s pts/6 00:00:00 0see: terminal
S 502 25355 25354 0 80 0 600 1055 poll_s pts/6 00:00:00 ping
$
}}}
<<<
!!Example 4 (Tee I/O)
Under certain cases, the output of a command might need to saved even after a terminal hangup as long as the command is running. This can be achieved using the options {{{-1}}} and {{{-2}}}.
<<<
{{{
$ 0see -N mytest -1 mytest.out -2- ./test_app test_args
...
}}}
<<<
While joining the command, the same set of arguments have to be specified as shown below. This is because, by redirecting the output and error to a file, we are changing the default behavior of output/error not going to terminal.
<<<
{{{
$ 0see -N mytest -1 mytest.out -2- -j
...
}}}
<<<
!Uninstallation
Run the command ''0see-clean'' to remove all the installed files. If there are any 0see terminals running, they will continue to run until exit. Any directories created by 0see have to be manually removed. The default directory used is {{{~/.0rt/0see}}} but if you have specified custom directories using {{{-h}}} option then you might need to clean them up as well.
[[0see Introduction|Introducing 0see]]
[[0see Manual|0see Manual]]
[[0see Options|0see Options]]
----
[[SSH Introduction|SSH - Secure Shell]]
[[SSH Data Types|SSHDataType]]
[[SSH Version String|SSHTransVersionString]]
[[SSH Key Exchange|SSHTransKex]]
[[SSH DH Group Exch..|SSHDHGEX]]
[[SSH Compute Keys|SSHTransComputeKeys]]
[[SSH Encrypt/Decrypt|SSHTransCrypto]]
----
!Secure Shell (SSH)
SSH is an application protocol used for secure communication with remote hosts. If you are new to SSH, you can find more details in [[wikipedia|http://en.wikipedia.org/wiki/Secure_Shell]]. In this wiki page, we will look into the details of how the protocol works and the things you should watch for while implementing your own client. One should note that we discuss ONLY SSH version 2.0.
We will not get into the details of implementing a SSH server but information mentioned here might be helpful.
!!Reference Documents (~RFCs)
The protocol has been explained in detail in the following ~RFCs,
#[[RFC 4250 - SSH Protocol Numbers|http://tools.ietf.org/html/rfc4250]]
#[[RFC 4251 - SSH Protocol Architecture|http://tools.ietf.org/html/rfc4251]]
#[[RFC 4253 - SSH Transport Layer Protocol|http://tools.ietf.org/html/rfc4253]]
#[[RFC 4252 - SSH Authentication Protocol|http://tools.ietf.org/html/rfc4252]]
#[[RFC 4254 - SSH Connection Protocol|http://tools.ietf.org/html/rfc4254]]
The above mentioned documents are sufficient for understanding the working of SSH. But when it comes to implementing it, we need to be aware of the extensions to the original documents that are currently being used. Such as the newer algorithms etc. Following are some of the extensions,
#[[RFC 4344 - SSH Encryption Modes - CTR Mode|http://tools.ietf.org/html/rfc4344]]
#[[RFC 4419 - SSH & Diffie-Hellman Group Exchange Protocol|http://tools.ietf.org/html/rfc4419]]
#[[RFC 4716 - SSH Public Key Format|http://tools.ietf.org/html/rfc4716]]
!!SSH Architecture
The image below gives an overview of the layers in SSH and the format of the binary packet used for communication between the server and the client.
<html><img src="imgs/ssh-arch.png" height="450"></html>
As you can see, there are 3 predominant layers in SSH, namely,
#''Transport Layer''
#''User Authentication Layer''
#''Connection Layer''
All the 3 layers work together in establishing and maintaining a secure connection.
As for the second diagram on the right, It indicates, what is termed as ''Binary Packet'', the format used for communication. The //random padding// is added to prevent traffic analysis attacks and the //MAC// is added only after establishing the keys. One important thing to note is that the padding should be added in such a way that the total size of the binary packet without //MAC// is 8 bytes or cipher block size (whichever is larger). After an encrypted key is found, everything in the binary packet including the //packet length// but excluding the //MAC// is encrypted.
!!Data Types
The following are the various datatypes used in SSH Packets,
|>|>|>|>|>|!{{{DATA TYPES}}}|
| [[byte|SSHDataType]] | [[boolean|SSHDataType]] | [[uint32|SSHDataType]] | [[uint64|SSHDataType]] | [[string|SSHDataType]] | [[mpint|SSHDataType]] |
!!Transport Layer
Some of the operations performed by transport layer have been listed below. They have been discussed in detail in their respective sections.
*[[Version String|SSHTransVersionString]]
*[[Key Exchange|SSHTransKex]]
*[[Compute Keys|SSHTransComputeKeys]]
*[[Encryption & Decryption|SSHTransCrypto]]
Due to lack of time, this document remains incomplete. MAC Computation is also part of the transport layer. Once the transport is established, the User Authentication layer performs Password or Public Key authentication or other supported authentication mechanisms. Once authenticated, user can open a channel to create a shell (or) run a command (or) forward ports and perform other similar tasks. The channels are the important part of the connection layer and multiple channels can be open inside a single SSH session.
/% Commenting out sections below this. They are all empty tiddler links.
*[[MAC Computation|SSHTransMACCompute]]
*[[Miscellaneous function|SSHTransMisc]]
!!User Authentication Layer
Two predominantly used user authentication methods will be explained here,
*[[Password|SSHUserAuthPass]]
*[[Public Key|SSHUserAuthPK]]
!!Connection Layer
Some of the application level requests supported by SSH are listed below,
*[[Global Requests|SSHConnGlobalReq]]
*[[Channel|SSHConnChannel]]
*[[Channel Requests|SSHConnChannelReq]]
*[[Channel Windows & Data|SSHConnChannelData]]
*[[Channel TCP/IP Port Forwarding|SSHConnChannelTCP]]
%/
!SSH - Diffie Hellman Group Key Exchange - Protocol
In this section we look into the details as mentioned in [[RFC 4419|http://tools.ietf.org/html/rfc4419]]. Since the generation of large numbers required by the algorithm can be quite tedious, we also provide a few examples of how this algorithm can be implemented using [[Openssl|http://www.openssl.org/]].
The diagram below gives you a technical overview of how the algorithm works. If you know how RSA works, this should be very easy to understand.
<html><center><img src="imgs/ssh-dhgex.png" height=500></center></html>
The steps mentioned in the above diagram is implemented through four SSH messages namely,
* {{{SSH_MSG_KEX_DH_GEX_REQUEST}}}
* {{{SSH_MSG_KEX_DH_GEX_GROUP}}}
* {{{SSH_MSG_KEX_DH_GEX_INIT}}}
* {{{SSH_MSG_KEX_DH_GEX_REPLY}}}
We look at them in detail below.
!!Client's Group Exchange Request
The entire key exchange mechanism is initiated by the client through this request message. The format of this message is as follows,
|!Data Type|!Value|!Comments|
| [[byte|SSHDataType]] | {{{SSH_MSG_KEX_DH_GEX_REQUEST}}} | |
| [[uint32|SSHDataType]] | Minimum Key Size |SHOULD be atleast 1024 bits |
| [[uint32|SSHDataType]] | Preferred Key Size |Preferred by Client |
| [[uint32|SSHDataType]] | Maximum Key Size |Usually 8192 bits |
!!Server's Group
The server responds to the client's request with a prime P and generator G, corresponding to the group, in the following format,
|!Data Type|!Value|!Comments|
| [[byte|SSHDataType]] | {{{SSH_MSG_KEX_DH_GEX_GROUP}}} | |
| [[mpint|SSHDataType]] | P, safe prime | |
| [[mpint|SSHDataType]] | G, generator | |
!!Client's Initializer
The client generates a random number X and computes {{{E = G^X mod P}}}. E is then sent across to the server. Do note that ''X should be kept a secret.''
|!Data Type|!Value|!Comments|
| [[byte|SSHDataType]] | {{{SSH_MSG_KEX_DH_GEX_INIT}}} | |
| [[mpint|SSHDataType]] | E | |
!!Server's Reply
Server generates a random number Y and computes {{{F = G^Y mod P}}} and shared secret {{{K = E^Y mod P}}}. It then computes exchange hash H (see below) and creates a signature 's' with the server's private host key using ~SHA1. ''~SHA1 is used as a hash function in case of both RSA and DSS''. And, Do note that ''Y, K and H should be kept a secret''.
|!Data Type|!Value|!Comments|
| [[byte|SSHDataType]] | {{{SSH_MSG_KEX_DH_GEX_INIT}}} | |
| [[string|SSHDataType]] | Server's Public Host Key |Format of Public Key is explained [[here|SSHPublicKeyFormat]] |
| [[mpint|SSHDataType]] | F | |
| [[string|SSHDataType]] | Signature of H |Format of Signature is explained [[here|SSHSignatureBlob]]|
!!Exchange Hash ''H''
The exchange hash is a hash of the following fields in the specified order. On the client, after receiving the Reply message, K is calculated as {{{F^X mod P}}}.
|!Data Type|!Value|!Comments|
| [[string|SSHDataType]] | ''~V_C'' |[[Client's Version String|SSHTransVersionString]] |
| [[string|SSHDataType]] | ''~V_S'' |[[Server's Version String|SSHTransVersionString]] |
| [[string|SSHDataType]] | ''~I_C'' |[[Client's SSH_MSG_KEXINIT|SSHTransKex]] i.e, the payload in binary packet |
| [[string|SSHDataType]] | ''~I_S'' |[[Server's SSH_MSG_KEXINIT|SSHTransKex]] i.e, the payload in binary packet |
| [[string|SSHDataType]] | ''~K_S'' |Server's Public Host Key (see above) |
| [[uint32|SSHDataType]] | ''minimum'' |as per Client's request message (see above) |
| [[uint32|SSHDataType]] | ''preferred'' |as per Client's request message (see above) |
| [[uint32|SSHDataType]] | ''maximum'' |as per Client's request message (see above) |
| [[mpint|SSHDataType]] | ''P'' | |
| [[mpint|SSHDataType]] | ''G'' | |
| [[mpint|SSHDataType]] | ''E'' | |
| [[mpint|SSHDataType]] | ''F'' | |
| [[mpint|SSHDataType]] | ''K'' | |
The hash function used varies based on the algorithms,
|!Group Exchange Algorithm|!Hash Function|
|diffie-hellman-group-exchange-sha1| ''~SHA1'' |
|diffie-hellman-group-exchange-sha256| ''~SHA-256'' |
!Diffie Hellman using ~OpenSSL
Openssl library provides a very easy interface for Diffie Hellman. A pseudo code has been listed below,
{{{
#include <openssl/rsa.h>
#include <openssl/dh.h>
DH *dh = DH_new();
RSA *rsa = RSA_new();
//// PART 1: Compute H
dh->p = BN_bin2bn(P, P_len, 0); // Convert P from binary to BIGNUM*
dh->g = BN_bin2bn(G, P_len, 0); // Convert G from binary to BIGNUM*
assert(DH_generate_key(dh)==1); // generates X and computes E
int E_len = BN_num_bytes(dh->pub_key); // dh->pub_key is of type BIGNUM*
unsigned char* E = malloc(E_len + 1);
assert( (E_len = BN_bn2bin(dh->pub_key, E)) > 0); // Convert BIGNUM to binary
/* NOTE: Since E is of type MPInt, a leading Zero byte might be required. */
// Compute K with F
int K_len = 0;
unsigned char* K = NULL;
BIGNUM *f = BN_bin2bn(F, F_len, 0);
K_len = DH_size(dh);
K = malloc(F_len + 1);
K_len = DH_compute_key(K, f, dh);
assert(K > 0);
/* NOTE: Since K is of type MPInt, a leading Zero byte might be required. */
//// PART 2: Verifying RSA Signature of H
unsigned char HSHA1[EVP_MAX_MD_SIZE];
unsigned int HSHA1_len = SHA1(H, H_len, HSHA1);
assert(HSHA1_len > 0);
// 'e' here is the public exponent for RSA key
// 'n' is the modulus
// Both 'e' and 'n' are extracted from Server's Public Key K_S
BIGNUM *e = BN_bin2bn(K_S_e, K_S_e_len, 0);
BIGNUM *n = BN_bin2bn(K_S_n, K_S_n_len, 0);
rsa->e = e;
rsa->n = n;
rsa->p = rsa->d = rsa->q = rsa->dmp1 = rsa->dmq1 = rsa->iqmp = NULL;
// NID_sha1 indicates our digest is of type SHA1
// H_sig is the signature to verify with Public Key
assert(RSA_verify(NID_sha1, HSHA1, HSHA1_len, H_sig, H_sig_len, rsa) == 1);
}}}
Data transmitted between the SSH client and server is in the format of SSH Packets which can contain one or more data fields. These fields are of one of the following data types.
!Byte
A field can be mentioned to contain one or more bytes. This is used only when the field is of fixed length. For example, some fields might be mentioned as {{{byte[16]}}} which indicates that it has a fixed length of 16 bytes. A field mentioned as {{{bytes[n]}}} might have variable {{{n}}} which is decided during a [[key exchange|SSHTransKex]] based on the algorithm chosen.
!Boolean
Data type boolean is used to indicate TRUE or FALSE. It is represented as a single byte with 0 indicating FALSE and 1 or Non-zero value indicating TRUE. Although, It is recommended that a value of 1 be used to indicate TRUE.
!Uint32
An unsigned integer represented in 32-bits in //Network-byte order// i.e, Big Endian.
!Uint64
An unsigned integer represented in 64-bits in //Network-byte order// i.e, Big Endian.
!String
A string is a representation of binary data along with its length. It can be mentioned using the other datatypes as follows,
|!Data Type|!Variable|
| uint32 | length |
| byte[length] | value |
| ''string'' | ''length'' + ''value'' |
!~MPInt
~MPInt stands for Multi Precision Integer ie., a Big Integer. It is represented in Two's complement format. It is stored as a string with 8-bits per byte. The lower address holds the most significant bit similar to Network-byte order or Big Endian byte order. An additional note to remember is that this is a ''signed integer'' so a large positive integer with most significant bit set to 1 should be preceded by a Zero byte to keep it positive.
The format is the same as {{{string}}}, a few examples have been mentioned below,
|!Integer|!Size(Hex)|!Value (Hex)|!~MPInt Representation (Hex Code)|
| 128 | 0x00 00 00 01 | 0x80 | 0x 00 00 00 02 00 80 |
| -128 | 0x00 00 00 01 | 0x80 | 0x 00 00 00 01 80 |
!~NameList
Namelist is a unique data type specifically designed to hold {{{,}}} seperated values. The format is exactly the same as {{{string}}}. An example has been mentioned below,
|!Size(bytes)|!Value(ASCII)|!~NameList(Hex+ASCII)|
| 13 |hmac-md5,none| 00 00 00 0D hmac-md5,none |
!SSH - Compute Keys
In this section, we look at how the various keys used in SSH are computed. If you haven't read through the [[key exchange|SSHTransKex]], now would be a good time. As you know, after the key exchange, we are left with a shared secret K and an exchange hash H. To compute the keys, one other important parameter used is the Session ID. The Session ID is nothing but the exchange hash H from the first key exchange. As per the specification, it is recommended that the key exchange takes place every 60 minutes or a transfer of 1 GB.
The following table lists the {{{HASH}}} function used for various key exchange algorithms. Please note, the same hash function is used for computing the keys.
|!Key Exchange Algorithm|!{{{HASH}}}|
|diffie-hellman-group1-sha1| ~SHA-1 |
|diffie-hellman-group14-sha1| ~SHA-1 |
|diffie-hellman-group-exchange-sha1| ~SHA-1 |
|diffie-hellman-group-exchange-sha256| ~SHA-256 |
Using the values of K, H, Session ID and a constant byte.
| !Variable | !Data Type |
|K| [[mpint|SSHDataType]] |
|H| {{{raw}}} |
|constant| [[byte|SSHDataType]] |
|Session ID| {{{raw}}} |
''NOTE'': {{{raw}}} data type indicates using the bytes as they are.
The various keys are computed as follows,
| !Key Name | !Constant (1 byte) | !Computation | !Purpose |
| IV C to S | "A" ie., ASCII 65 | {{{HASH( K + H + "A" + Session ID )}}} | Initialization Vector used in Client to Server encryption |
| IV C to S | "B" ie., ASCII 66 | {{{HASH( K + H + "B" + Session ID )}}} | Initialization Vector used in Server to Client encryption |
| Encrypt C to S | "C" ie., ASCII 67 | {{{HASH( K + H + "C" + Session ID )}}} | Encryption Key used in Client to Server encryption |
| Encrypt S to C | "D" ie., ASCII 68 | {{{HASH( K + H + "D" + Session ID )}}} | Encryption Key used in Server to Client encryption |
| MAC Key C to S | "E" ie., ASCII 69 | {{{HASH( K + H + "E" + Session ID )}}} | MAC Key used in Client to Server integrity check |
| MAC Key S to C | "F" ie., ASCII 70 | {{{HASH( K + H + "F" + Session ID )}}} | MAC Key used in Server to Client integrity check |
It can so happen that the {{{HASH}}} function might not provide the necessary no. of bits required for an encryption key such as, {{{AES-192}}} uses a key size of 192 bits while {{{SHA-1}}} can create a hash of 160 bits. To get the extra bits, the following logic is used,
| !Iterations| Key Calculation |
| K1 | {{{HASH( K + H + constant + Session ID ) }}} |
| K2 | {{{HASH( K + H + K1 ) }}} |
| K3 | {{{HASH( K + H + K1 + K2 ) }}} |
| {{{....}}} | {{{....}}} |
|>| {{{key = K1 + K2 + K3 + ...}}} |
!SSH Encryption & Decryption
In this section we get into the details of reading data out of a connection along with their encryption and decryption methods. It is important to know how one should read the data because all parts of the binary packet except the MAC is encrypted. This means __decryption has to be done in two parts__, one to find the packet length while the other for the rest of the packet.
!Data Read
After accepting the respective version strings, the connection will be in plain text until [[key exchange|SSHTransKex]] is complete. But one should note that SSH specification demands that the padding in binary packet is done in such a way that before key exchange is completed, the size of the binary packet excluding mac is a multiple of 8. So essentially during implementation, it would be wise to have the ''first read'' to be of either ''8 bytes or cipher BLOCK SIZE'' for easier understanding.
!Encryption
Here we will discuss only CTR mode of operation as per [[RFC 4344|http://tools.ietf.org/html/rfc4344]]. It is a very convenient yet secure encryption mode. The following diagram gives an overview,
<html><img src="imgs/ssh-aes-enc-dec.png"></html>
As you can see, we encrypt only the counter in SSH's CTR mode. Since the [[Initialization Vector (IV)|SSHTransComputeKeys]] obtained during key exchange is a shared secret, this is reasonably secure. But when added with a periodic key exchange, ie., after 1 hour or 1 GB, that SSH mandates, this can be considered acceptable. Since we encrypt only the counter, the decryption process remains exactly the same. In the above diagram, the {{{Packet Block}}} and {{{Cipher Block}}} will change places for decryption process.
!SSH Transport Protocol - Key Exchange Mechanism
After receiving the version string and ensuring that SSH version 2.0 is supported, both client and server start the key exchange process. The key exchange process has two parts to it,
* Identifying the algorithms to use.
* Running through the key exchange algorithm.
The algorithms for key exchange, encryption, mac and compression are identified through the SSH message {{{SSH_MSG_KEXINIT}}}. The format of this messages is as follows,
| !Data Type | !Value |
| [[byte|SSHDataType]] |{{{SSH_MSG_KEXINIT}}} |
| [[byte[16]|SSHDataType]] |Random Bytes |
| [[namelist|SSHDataType]] |Key Exchange Algorithms |
| [[namelist|SSHDataType]] |Server Host Key Algorithms |
| [[namelist|SSHDataType]] |Client to Server Encryption Algorithms |
| [[namelist|SSHDataType]] |Server to Client Encryption Algorithms |
| [[namelist|SSHDataType]] |Client to Server MAC Algorithms |
| [[namelist|SSHDataType]] |Server to Client MAC Algorithms |
| [[namelist|SSHDataType]] |Client to Server Compression Algorithms |
| [[namelist|SSHDataType]] |Server to Client Compression Algorithms |
| [[namelist|SSHDataType]] |Client to Server languages (?) |
| [[namelist|SSHDataType]] |Server to Client languages (?) |
| [[boolean|SSHDataType]] |First Key Exchange packet follows |
| [[uint32|SSHDataType]] |0 (future use) |
After receiving this message, choose the first client algorithm that is accepted by the server. In case of failure to find a match, then the connection is closed.
!Key Exchange Algorithms
After the {{{SSH_MSG_KEXINIT}}} message, an algorithm specific key exchange takes place. The following are some of the SSH specified Key Exchange algorithms,
|!Algorithm Name|!Hash Method|!Reference|
|''diffie-hellman-group1-sha1''| {{{SHA-1}}} | [[FIPS 180-2|http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf]], [[RFC 4253|http://tools.ietf.org/html/rfc4253]] |
|''diffie-hellman-group14-sha1''| {{{SHA-1}}} | [[FIPS 180-2|http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf]], [[RFC 4253|http://tools.ietf.org/html/rfc4253]] |
|[[diffie-hellman-group-exchange-sha1|SSHDHGEX]]| {{{SHA-1}}} | [[FIPS 180-2|http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf]], [[RFC 4419|http://tools.ietf.org/html/rfc4419]] |
|[[diffie-hellman-group-exchange-sha256|SSHDHGEX]]| {{{SHA-256}}} | [[FIPS 180-2|http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf]], [[RFC 4419|http://tools.ietf.org/html/rfc4419]] |
Diffie Hellman Group Exchange has been explained in detailed [[here|SSHDHGEX]].
!Key Exchange Completion
The key exchange process is considered complete after either side sending the message {{{SSH_MSG_NEWKEYS}}}. After sending this message, either side should start using the new algorithms and keys for sending subsequent messages. And, after receiving this message, either side should use the new algorithms and keys for receiving any further messages.
The format of the message is simple and as follows,
|!Data Type|!Value|!Comments|
| [[byte|SSHDataType]] | {{{SSH_MSG_NEWKEYS}}} | The entire message is just one byte |
After establishing the connection between client and server. It is required that both client and server send a plain text string terminated by {{{\r\n}}}. This string is called ''Version String''. Do note that, any data following this should adhere to the Binary Packet format mentioned in the [[Overview|SSH - Secure Shell]] page.
The format of the //version string// is as follows,
|!SSH|!-|!Protocol Version|!-|!Software Version|!{{{SPACE}}}|!Comments|
| SSH | - | 2.0 | - | ~OpenSSH_5.2 | | |
|>|>|>|>|>|>|ie., ~SSH-2.0-~OpenSSH_5.2|
It is important to remember the version string for the connection established i.e, both the client and server's version strings. It will be later used in the computation of the cryptographic keys.
After receiving the version string, Both client and server should maintain a ''sequence number'' starting at 0. The sequence number is incremented after sending a binary packet. This sequence number is used later in MAC computation.
Type the text for 'Secure Shell'
/***
|Name|SinglePageModePlugin|
|Source|http://www.TiddlyTools.com/#SinglePageModePlugin|
|Documentation|http://www.TiddlyTools.com/#SinglePageModePluginInfo|
|Version|2.9.7|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Show tiddlers one at a time with automatic permalink, or always open tiddlers at top/bottom of page.|
This plugin allows you to configure TiddlyWiki to navigate more like a traditional multipage web site with only one tiddler displayed at a time.
!!!!!Documentation
>see [[SinglePageModePluginInfo]]
!!!!!Configuration
<<<
<<option chkSinglePageMode>> Display one tiddler at a time
><<option chkSinglePagePermalink>> Automatically permalink current tiddler
><<option chkSinglePageKeepFoldedTiddlers>> Don't close tiddlers that are folded
><<option chkSinglePageKeepEditedTiddlers>> Don't close tiddlers that are being edited
<<option chkTopOfPageMode>> Open tiddlers at the top of the page
<<option chkBottomOfPageMode>> Open tiddlers at the bottom of the page
<<option chkSinglePageAutoScroll>> Automatically scroll tiddler into view (if needed)
Notes:
* The "display one tiddler at a time" option can also be //temporarily// set/reset by including a 'paramifier' in the document URL: {{{#SPM:true}}} or {{{#SPM:false}}}.
* If more than one display mode is selected, 'one at a time' display takes precedence over both 'top' and 'bottom' settings, and if 'one at a time' setting is not used, 'top of page' takes precedence over 'bottom of page'.
* When using Apple's Safari browser, automatically setting the permalink causes an error and is disabled.
<<<
!!!!!Revisions
<<<
2010.11.30 2.9.7 use story.getTiddler()
2008.10.17 2.9.6 changed chkSinglePageAutoScroll default to false
| Please see [[SinglePageModePluginInfo]] for previous revision details |
2005.08.15 1.0.0 Initial Release. Support for BACK/FORWARD buttons adapted from code developed by Clint Checketts.
<<<
!!!!!Code
***/
//{{{
version.extensions.SinglePageModePlugin= {major: 2, minor: 9, revision: 7, date: new Date(2010,11,30)};
//}}}
//{{{
config.paramifiers.SPM = { onstart: function(v) {
config.options.chkSinglePageMode=eval(v);
if (config.options.chkSinglePageMode && config.options.chkSinglePagePermalink && !config.browser.isSafari) {
config.lastURL = window.location.hash;
if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
}
} };
//}}}
//{{{
if (config.options.chkSinglePageMode==undefined)
config.options.chkSinglePageMode=true;
if (config.options.chkSinglePagePermalink==undefined)
config.options.chkSinglePagePermalink=true;
if (config.options.chkSinglePageKeepFoldedTiddlers==undefined)
config.options.chkSinglePageKeepFoldedTiddlers=false;
if (config.options.chkSinglePageKeepEditedTiddlers==undefined)
config.options.chkSinglePageKeepEditedTiddlers=false;
if (config.options.chkTopOfPageMode==undefined)
config.options.chkTopOfPageMode=false;
if (config.options.chkBottomOfPageMode==undefined)
config.options.chkBottomOfPageMode=false;
if (config.options.chkSinglePageAutoScroll==undefined)
config.options.chkSinglePageAutoScroll=false;
//}}}
//{{{
config.SPMTimer = 0;
config.lastURL = window.location.hash;
function checkLastURL()
{
if (!config.options.chkSinglePageMode)
{ window.clearInterval(config.SPMTimer); config.SPMTimer=0; return; }
if (config.lastURL == window.location.hash) return; // no change in hash
var tids=decodeURIComponent(window.location.hash.substr(1)).readBracketedList();
if (tids.length==1) // permalink (single tiddler in URL)
story.displayTiddler(null,tids[0]);
else { // restore permaview or default view
config.lastURL = window.location.hash;
if (!tids.length) tids=store.getTiddlerText("DefaultTiddlers").readBracketedList();
story.closeAllTiddlers();
story.displayTiddlers(null,tids);
}
}
if (Story.prototype.SPM_coreDisplayTiddler==undefined)
Story.prototype.SPM_coreDisplayTiddler=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,slowly)
{
var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
var tiddlerElem=story.getTiddler(title); // ==null unless tiddler is already displayed
var opt=config.options;
var single=opt.chkSinglePageMode && !startingUp;
var top=opt.chkTopOfPageMode && !startingUp;
var bottom=opt.chkBottomOfPageMode && !startingUp;
if (single) {
story.forEachTiddler(function(tid,elem) {
// skip current tiddler and, optionally, tiddlers that are folded.
if ( tid==title
|| (opt.chkSinglePageKeepFoldedTiddlers && elem.getAttribute("folded")=="true"))
return;
// if a tiddler is being edited, ask before closing
if (elem.getAttribute("dirty")=="true") {
if (opt.chkSinglePageKeepEditedTiddlers) return;
// if tiddler to be displayed is already shown, then leave active tiddler editor as is
// (occurs when switching between view and edit modes)
if (tiddlerElem) return;
// otherwise, ask for permission
var msg="'"+tid+"' is currently being edited.\n\n";
msg+="Press OK to save and close this tiddler\nor press Cancel to leave it opened";
if (!confirm(msg)) return; else story.saveTiddler(tid);
}
story.closeTiddler(tid);
});
}
else if (top)
arguments[0]=null;
else if (bottom)
arguments[0]="bottom";
if (single && opt.chkSinglePagePermalink && !config.browser.isSafari) {
window.location.hash = encodeURIComponent(String.encodeTiddlyLink(title));
config.lastURL = window.location.hash;
document.title = wikifyPlain("SiteTitle") + " - " + title;
if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
}
if (tiddlerElem && tiddlerElem.getAttribute("dirty")=="true") { // editing... move tiddler without re-rendering
var isTopTiddler=(tiddlerElem.previousSibling==null);
if (!isTopTiddler && (single || top))
tiddlerElem.parentNode.insertBefore(tiddlerElem,tiddlerElem.parentNode.firstChild);
else if (bottom)
tiddlerElem.parentNode.insertBefore(tiddlerElem,null);
else this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
} else
this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
var tiddlerElem=story.getTiddler(title);
if (tiddlerElem&&opt.chkSinglePageAutoScroll) {
// scroll to top of page or top of tiddler
var isTopTiddler=(tiddlerElem.previousSibling==null);
var yPos=isTopTiddler?0:ensureVisible(tiddlerElem);
// if animating, defer scroll until after animation completes
var delay=opt.chkAnimate?config.animDuration+10:0;
setTimeout("window.scrollTo(0,"+yPos+")",delay);
}
}
if (Story.prototype.SPM_coreDisplayTiddlers==undefined)
Story.prototype.SPM_coreDisplayTiddlers=Story.prototype.displayTiddlers;
Story.prototype.displayTiddlers = function() {
// suspend single/top/bottom modes when showing multiple tiddlers
var opt=config.options;
var saveSPM=opt.chkSinglePageMode; opt.chkSinglePageMode=false;
var saveTPM=opt.chkTopOfPageMode; opt.chkTopOfPageMode=false;
var saveBPM=opt.chkBottomOfPageMode; opt.chkBottomOfPageMode=false;
this.SPM_coreDisplayTiddlers.apply(this,arguments);
opt.chkBottomOfPageMode=saveBPM;
opt.chkTopOfPageMode=saveTPM;
opt.chkSinglePageMode=saveSPM;
}
//}}}
Documentation - Man Pages