support.tcl 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. #!/usr/bin/env tclsh
  2. #
  3. # Copyright (c) 2015-2017 OpenIndex.de
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. # THE SOFTWARE.
  22. #
  23. package require Tk
  24. package require msgcat
  25. namespace eval ::support {
  26. variable TITLE
  27. variable VERSION
  28. variable ICON
  29. variable WRAPPED
  30. variable APP_DIR
  31. variable DATA_DIR
  32. variable TEMP_DIR
  33. variable LOG_NAME
  34. variable MAC_APPLICATION_BUNDLE 0
  35. variable FRAME
  36. variable CONNECTED 0
  37. # Launch application.
  38. proc launch {dir wrapped version} {
  39. global env
  40. global tcl_platform
  41. variable MAC_APPLICATION_BUNDLE
  42. # Get application base path.
  43. if {$wrapped && [info exists env(MAC_APPLICATION_BUNDLE)]} {
  44. variable APP_DIR [file dirname [file dirname [file dirname [file dirname $dir]]]]
  45. variable LOG_NAME [file tail [file dirname [file dirname [file dirname $dir]]]]
  46. set MAC_APPLICATION_BUNDLE 1
  47. set pos [string last "." $LOG_NAME]
  48. if {$pos > 0} {
  49. set LOG_NAME [string range $LOG_NAME 0 [expr {$pos-1}]]
  50. }
  51. } elseif {$wrapped} {
  52. variable APP_DIR [file dirname $dir]
  53. variable LOG_NAME [file tail $dir]
  54. # Remove exe file extension from log name on Windows.
  55. if {[::support::utils::is_windows]} {
  56. set pos [string last "." $LOG_NAME]
  57. if {$pos > 0} {
  58. set LOG_NAME [string range $LOG_NAME 0 [expr {$pos-1}]]
  59. }
  60. }
  61. } else {
  62. variable APP_DIR $dir
  63. variable LOG_NAME "support"
  64. }
  65. # Init logging.
  66. set ::support::logger::FILE [file join $APP_DIR [format "%s.log" $LOG_NAME]]
  67. fconfigure stdout -buffering line
  68. fconfigure stderr -buffering line
  69. chan push stdout ::support::logger
  70. chan push stderr ::support::logger
  71. # Init translations.
  72. ::msgcat::mcload [file join $dir "lib" "app-support" "msgs"]
  73. # Set application settings.
  74. variable TITLE [_ "Remote Support Tool"]
  75. variable VERSION $version
  76. variable WRAPPED $wrapped
  77. variable DATA_DIR [file join $dir "data"]
  78. # Detect temporary directory.
  79. variable TEMP_DIR [file join [::support::utils::get_temp_dir] "temp-support-[pid]"]
  80. if {![file exists $TEMP_DIR]} {
  81. if {[catch {file mkdir $TEMP_DIR}]} {
  82. puts "ERROR: The temporary directory \"$TEMP_DIR\" was not created!"
  83. }
  84. } elseif {![file isdirectory $TEMP_DIR]} {
  85. puts "ERROR: The temporary directory \"$TEMP_DIR\" is not a directory!"
  86. }
  87. # Print some informations.
  88. puts ""
  89. puts "-----------------------------------------------------------------------------"
  90. puts " $TITLE $VERSION"
  91. puts "-----------------------------------------------------------------------------"
  92. puts " system : $tcl_platform(os) $tcl_platform(osVersion)"
  93. puts " machine : $tcl_platform(machine) / $tcl_platform(platform)"
  94. puts " host name : [info hostname]"
  95. puts " user name : $tcl_platform(user)"
  96. puts " user home : $env(HOME)"
  97. puts " tcl version : [info patchlevel]"
  98. if {[info exists tcl_platform(threaded)]} {
  99. puts " tcl threads : enabled"
  100. } else {
  101. puts " tcl threads : disabled"
  102. }
  103. puts " tcl executable : [info nameofexecutable]"
  104. puts " root directory : $dir"
  105. puts " work directory : [pwd]"
  106. puts " temp directory : $TEMP_DIR"
  107. puts "-----------------------------------------------------------------------------"
  108. puts "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR"
  109. puts "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,"
  110. puts "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE"
  111. puts "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER"
  112. puts "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,"
  113. puts "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN"
  114. puts "THE SOFTWARE."
  115. puts "-----------------------------------------------------------------------------"
  116. #parray env
  117. #puts "-----------------------------------------------------------------------------"
  118. # Load global configuration.
  119. set cfgFile [file join $DATA_DIR config_global.ini]
  120. if {$cfgFile != "" && [file isfile $cfgFile]} {
  121. ::support::Config::configure $cfgFile
  122. }
  123. # Load custom configuration.
  124. set cfgFile [file join $DATA_DIR config.ini]
  125. if {$cfgFile != "" && [file isfile $cfgFile]} {
  126. ::support::Config::configure $cfgFile
  127. }
  128. set cfgFile [file join $APP_DIR config.ini]
  129. if {$cfgFile != "" && [file isfile $cfgFile]} {
  130. puts "Load custom configuration from '$cfgFile'."
  131. ::support::Config::configure $cfgFile
  132. }
  133. # Load application icon.
  134. variable ICON [::support::utils::load_image_file icon.png]
  135. # Initialize VNC session.
  136. ::support::session::init
  137. if {[::support::utils::is_darwin]} {
  138. # Show settings window through the Mac OS X menubar.
  139. proc ::tk::mac::ShowPreferences {} {
  140. ::support::SettingsWindow::open
  141. }
  142. # Show about window through the Mac OS X menubar.
  143. proc ::tkAboutDialog {} {
  144. ::support::AboutWindow::open
  145. }
  146. }
  147. # Create main frame.
  148. ::support::ApplicationWindow::open
  149. # Mac OS X does not put the application window into foreground.
  150. # As long as we find no better solution, the application is put into
  151. # foreground via AppleScript.
  152. if {[::support::utils::is_darwin]} {
  153. set script "tell application \"System Events\"\n \
  154. set frontmost of the first process whose unix id is [pid] to true\n \
  155. end tell"
  156. if { [catch {exec osascript -e $script} result] } {
  157. puts "Can't put application window into foreground!"
  158. puts $::errorInfo
  159. }
  160. }
  161. }
  162. # Shutdown application.
  163. proc shutdown {} {
  164. exit
  165. }
  166. # Shorthand method for translation.
  167. proc translate {s args} {
  168. return [::msgcat::mc $s {*}$args]
  169. }
  170. # Create VNC connection.
  171. proc connect {} {
  172. variable CONNECTED
  173. ::support::ApplicationWindow::setStatusConnecting
  174. # Launch VNC session.
  175. set result [::support::session::start]
  176. if {$result != 1} {
  177. set CONNECTED 0
  178. puts "VNC connection failed!"
  179. ::support::ApplicationWindow::setStatusError
  180. return
  181. }
  182. # Register connection.
  183. set CONNECTED 1
  184. # Check for valid connection after some seconds.
  185. after 5000 ::support::ping 1
  186. }
  187. # Close VNC connection.
  188. proc disconnect {{force 0}} {
  189. variable CONNECTED 0
  190. if {$force == 1} {
  191. ::support::session::stop
  192. ::support::ApplicationWindow::setStatusDisconnected
  193. }
  194. }
  195. # Test, if a VNC session is currently running.
  196. proc ping {{firstPing 0}} {
  197. #puts "PING"
  198. variable CONNECTED
  199. set running 1
  200. if {$running == 1 && $CONNECTED != 1} {
  201. set running 0
  202. #puts "> VNC connection is closed"
  203. }
  204. if {$running == 1 && ![::support::session::is_running]} {
  205. set running 0
  206. #puts "> VNC is not running anymore"
  207. }
  208. if {$running == 1} {
  209. if {$firstPing == 1} {
  210. ::support::ApplicationWindow::setStatusConnected
  211. }
  212. # Again check for valid connection after some seconds.
  213. after 2500 ::support::ping
  214. } else {
  215. ::support::disconnect 1
  216. }
  217. }
  218. }
  219. # Handler that writes stdout & stderr into a separate file.
  220. namespace eval ::support::logger {
  221. variable FILE
  222. variable HANDLE
  223. proc clear {handle} {
  224. }
  225. proc finalize {handle} {
  226. variable HANDLE
  227. close $HANDLE
  228. unset HANDLE
  229. }
  230. proc initialize {handle mode} {
  231. variable FILE
  232. variable HANDLE
  233. if {![info exists HANDLE]} {
  234. set HANDLE [open $FILE w]
  235. }
  236. return {clear finalize initialize flush write}
  237. }
  238. proc flush {handle} {
  239. variable HANDLE
  240. ::flush $HANDLE
  241. }
  242. proc write {handle buffer} {
  243. variable HANDLE
  244. puts -nonewline $HANDLE $buffer
  245. flush $handle
  246. return $buffer
  247. }
  248. namespace export *
  249. namespace ensemble create
  250. }
  251. # Shorthand method for translations.
  252. proc _ {s args} {
  253. return [::support::translate $s {*}$args]
  254. }
  255. # Override exit method
  256. # to do some cleanups before shutdown.
  257. rename exit __exit
  258. proc exit {} {
  259. puts "Shutdown application. Have a nice day!"
  260. if {$::support::CONNECTED == 1} {
  261. ::support::disconnect 1
  262. }
  263. # Remove temporary files explicitly.
  264. foreach f [::support::utils::get_files $::support::TEMP_DIR] {
  265. if {[file isfile $f] && [catch {file delete -force $f}]} {
  266. puts "WARNING: Can't remove temporary file \"$f\"!"
  267. puts $::errorInfo
  268. }
  269. }
  270. # Remove temporary folder recursively.
  271. if {[catch {file delete -force $::support::TEMP_DIR}]} {
  272. puts "WARNING: Can't cleanup temporary directory \"$::support::TEMP_DIR\"!"
  273. puts $::errorInfo
  274. }
  275. __exit
  276. }