rsence 2.0.0.0.pre → 2.0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
data/lib/conf/argv.rb ADDED
@@ -0,0 +1,664 @@
1
+ #--
2
+ ## Riassence Framework
3
+ # Copyright 2010 Riassence Inc.
4
+ # http://riassence.com/
5
+ #
6
+ # You should have received a copy of the GNU General Public License along
7
+ # with this software package. If not, contact licensing@riassence.com
8
+ ##
9
+ #++
10
+
11
+ module RSence
12
+ def self.pid_support?
13
+ # true for non-windows
14
+ return (not ['i386-mingw32','x86-mingw32'].include?(RUBY_PLATFORM))
15
+ end
16
+ class ARGVParser
17
+
18
+ @@version = File.read( File.join( SERVER_PATH, 'VERSION' ) ).strip
19
+
20
+ if RSence.pid_support?
21
+ @@cmds = [ :run, :status, :start, :stop, :restart, :save, :setup,
22
+ :initenv, :version, :help ]
23
+ else
24
+ @@cmds = [ :run, :setup, :initenv, :version, :help ]
25
+ end
26
+
27
+ @@cmd_help = {}
28
+
29
+ @@cmd_help[:head] = <<-EOF
30
+ usage: rsence <command> [options] [args]
31
+ RSence command-line tool, version #{@@version}
32
+ EOF
33
+
34
+ @@cmd_help[:unknown] = "Unknown command: "
35
+
36
+ @@cmd_help[:help_help] = "Type 'rsence help' for usage."
37
+
38
+ @@cmd_help[:help_main] = <<-EOF
39
+ Type 'rsence help <command>' for help on a specific command.
40
+
41
+ Available commands:
42
+ #{@@cmds.map{|cmd|cmd.to_s}.join("\n ")}
43
+
44
+ EOF
45
+
46
+ @@cmd_help[:path] = <<-EOF
47
+ The [PATH] is the RSence environment to use.
48
+ The [PATH] defaults to the current working directory.
49
+
50
+ The expected structure of a project environment (where 'project_directory'
51
+ is the directory of your project) is:
52
+
53
+ [dir] project_directory :: The directory of your project.
54
+ [dir] conf :: The directory of config files.
55
+ [file] config.yaml :: The config file to load by defult.
56
+ [dir] db :: Directory containing database files.
57
+ [dir] log :: Directory containing log files.
58
+ [dir] plugins :: Directory containing installed plugins.
59
+ [dir] run :: Directory containing runtime pid files.
60
+
61
+ The 'config.yaml' file contains patches specific to your project.
62
+
63
+ The configuration files are loaded and applied in this order:
64
+ 1: [rsence_install_path]/conf/default_conf.yaml
65
+ 2: [rsence_install_path]/conf/local_conf.yaml
66
+ 3: /etc/rsence/config.yaml
67
+ 4: ~/.rsence/config.yaml
68
+ 5: [project_directory]/conf/config.yaml
69
+ 6: Any files given using --conf parameters, in order of occurrence.
70
+
71
+ The plugins directory contains the plugins installed in the project.
72
+
73
+ See also the 'setup' and 'initenv' commands.
74
+ EOF
75
+
76
+ @@cmd_help[:options] = <<-EOF
77
+ Available options:
78
+
79
+ --conf <file.yaml> Use additional config file. You can give this option
80
+ several times. The <file.yaml> is the configuration
81
+ file to load.
82
+
83
+ --debug (-d) Debug mode. Shortcut for several options useful for
84
+ developers. Not the preferred mode for production.
85
+
86
+ --verbose (-v) More verbose output. Also enabled by --debug
87
+
88
+ --log-fg (-f) Directs the output of the log messages to stdout
89
+ instead of the log files. Useful for development.
90
+
91
+ --trace-js Logs all js/json going through the msg object.
92
+
93
+ --trace-delegate Logs all plugin delegate calls.
94
+
95
+ --port <number> The port number the http server listens to.
96
+
97
+ --addr <ip address> The IP address or netmask the http server listens to.
98
+ '0.0.0.0' matches all interfaces.
99
+ '127.0.0.1' matches the local loopback interface.
100
+
101
+ --server <handler> The Rack handler to use.
102
+
103
+ --reset-sessions (-r) Resets all active sessions.
104
+
105
+ --auto-update (-a) Automatically checks for changes in installed plugin
106
+ and component bundles. Rebuilds changed component
107
+ bundles and reload changed plugin bundles.
108
+ Useful for development purposes.
109
+ Also enabled by --debug
110
+
111
+ --latency <number> Sleeps <number> amount of milliseconds on every
112
+ request. Useful for testing slow connections.
113
+
114
+ --say (-S) Uses speech synthesis via the 'say' command to
115
+ provide audible feedback, when --auto-update is
116
+ enabled. Says 'Autobuild complete.',
117
+ 'Loaded [plugin name].', 'Unloaded [plugin name].',
118
+ 'Reloaded [plugin name].', 'Reloading plugins.' and
119
+ 'Plugins reloaded.'
120
+ Only available on Mac OS X and other systems with a
121
+ 'say' command installed.
122
+
123
+ EOF
124
+
125
+ @@cmd_help[:run] = <<-EOF
126
+ usage: 'rsence run [options] [PATH]'
127
+
128
+ The 'run' command starts RSence in foreground (no daemon). Exit with CTRL-C.
129
+ This is the suggested mode for development and the only mode supported by
130
+ Windows, because Windows is missing the concept of a process ID.
131
+
132
+ #{@@cmd_help[:path]}
133
+ #{@@cmd_help[:options]}
134
+ EOF
135
+
136
+ @@cmd_help[:start] = <<-EOF
137
+ usage: 'rsence start [options] [PATH]'
138
+
139
+ The 'start' command starts RSence in the background (as a daemon).
140
+
141
+ Use the 'stop' command to stop RSence.
142
+
143
+ Use the 'restart' command to restart RSence in the background.
144
+
145
+ Use the 'status' command to check if RSence is running.
146
+
147
+ #{@@cmd_help[:path]}
148
+ #{@@cmd_help[:options]}
149
+ EOF
150
+
151
+ @@cmd_help[:stop] = <<-EOF
152
+ usage: 'rsence stop [options] [PATH]'
153
+
154
+ The 'stop' command stops RSence running in the background (as a daemon).
155
+
156
+ Use the 'status' command to check if RSence is running.
157
+
158
+ #{@@cmd_help[:path]}
159
+ #{@@cmd_help[:options]}
160
+ EOF
161
+
162
+ @@cmd_help[:restart] = <<-EOF
163
+ usage: 'rsence restart [options] [PATH]'
164
+
165
+ The 'restart' command restarts RSence in the background (as a daemon).
166
+ If RSence wasn't running before the 'restart' command was issued, the
167
+ effect is the same as 'start'.
168
+
169
+ Use the 'stop' command to stop RSence.
170
+ Use the 'status' command to check if RSence is running.
171
+
172
+ #{@@cmd_help[:path]}
173
+ #{@@cmd_help[:options]}
174
+ EOF
175
+
176
+ @@cmd_help[:status] = <<-EOF
177
+ usage: 'rsence status [options] [PATH]'
178
+
179
+ The 'status' command checks if RSence is running.
180
+ If started with the 'start', 'run' or 'restart' command, a PID file is written.
181
+ Status checks if the PID file exists, if the RSence process responds and if
182
+ the configured TCP port responds in the configured IP address.
183
+
184
+ Available options:
185
+
186
+ --conf <file.yaml> Use additional config file. You can give this option
187
+ several times. The <file.yaml> is the configuration
188
+ file to load.
189
+
190
+ --debug (-d) Debug mode. Shortcut for several options useful for
191
+ developers. Not the preferred mode for production.
192
+
193
+ --verbose (-v) More verbose output. Also enabled by --debug
194
+
195
+ --port <number> The port number the http server listens to.
196
+
197
+ --addr <ip address> The IP address or netmask the http server listens to.
198
+
199
+ #{@@cmd_help[:path]}
200
+
201
+ EOF
202
+
203
+ @@cmd_help[:tail] = <<-EOF
204
+ RSence is a self-contained rich internet application client-server framework.
205
+ For further information, see http://rsence.org/
206
+ EOF
207
+
208
+ def initialize( argv )
209
+ @argv = argv
210
+ @startable = false
211
+ parse_argv
212
+ end
213
+
214
+ def startable?; @startable; end
215
+
216
+ def parse_help_argv
217
+ if @argv.length >= 2
218
+ help_cmd = @argv[1].to_sym
219
+ else
220
+ help_cmd = :help_main
221
+ end
222
+ help( help_cmd )
223
+ exit
224
+ end
225
+
226
+ def init_args
227
+ @args = {
228
+ :env_path => Dir.pwd,
229
+ :conf_files => [ ], # --conf
230
+ :debug => false, # -d --debug
231
+ :verbose => false, # -v --verbose
232
+ :log_fg => false, # -f --log-fg
233
+ :trace_js => false, # --trace-js
234
+ :trace_delegate => false, # --trace-delegate
235
+ :port => nil, # --port
236
+ :addr => nil, # --addr --bind
237
+ :server => nil, # --server
238
+ :reset_ses => false, # -r --reset-sessions
239
+ :autoupdate => false, # -a --auto-update
240
+ :latency => 0, # --latency
241
+ :say => false, # -S --say
242
+
243
+ # client_pkg (not supported yet)
244
+ :client_pkg_no_gzip => false, # --build-no-gzip
245
+ :client_pkg_no_obfuscation => false, # --build-no-obfuscation
246
+ :client_pkg_no_whitespace_removal => false, # --build-keep-whitespace
247
+ :client_pkg_quiet => true, # --build-verbose
248
+
249
+ }
250
+ end
251
+
252
+ def parse_startup_argv
253
+ init_args
254
+ expect_option = false
255
+ option_name = false
256
+ if @argv.length >= 2
257
+ @argv[1..-1].each_with_index do |arg,i|
258
+ if expect_option
259
+ if [:port,:latency].include?(option_name) and arg.to_i.to_s != arg
260
+ puts "invalid #{option_nam.to_s}, expected number: #{arg.inspect}"
261
+ puts "Type 'rsence help #{@cmd.to_s}' for usage."
262
+ exit
263
+ elsif option_name == :conf_files
264
+ if not File.exists?( arg ) or not File.file?( arg )
265
+ puts "no such configuration file: #{arg.inspect}"
266
+ puts "Type 'rsence help #{@cmd.to_s}' for usage."
267
+ exit
268
+ else
269
+ @args[:conf_files].push( arg )
270
+ end
271
+ else
272
+ @args[option_name] = arg
273
+ end
274
+ expect_option = false
275
+ else
276
+ if arg.start_with?('--')
277
+ if arg == '--debug'
278
+ set_debug
279
+ elsif arg == '--verbose'
280
+ set_verbose
281
+ elsif arg == '--log-fg'
282
+ set_log_fg
283
+ elsif arg == '--trace-js'
284
+ @args[:trace_js] = true
285
+ elsif arg == '--trace-delegate'
286
+ @args[:trace_delegate] = true
287
+ elsif arg == '--port'
288
+ expect_option = true
289
+ option_name = :port
290
+ elsif arg == '--addr'
291
+ expect_option = true
292
+ option_name = :addr
293
+ elsif arg == '--server'
294
+ expect_option = true
295
+ option_name = :server
296
+ elsif arg == '--conf' or arg == '--config'
297
+ expect_option = true
298
+ option_name = :conf_files
299
+ elsif arg == '--reset-sessions'
300
+ set_reset_ses
301
+ elsif arg == '--auto-update'
302
+ set_autoupdate
303
+ elsif arg == '--latency'
304
+ expect_option = true
305
+ option_name = :latency
306
+ elsif arg == '--say'
307
+ set_say
308
+ else
309
+ invalid_option(arg)
310
+ end
311
+ elsif arg.start_with?('-')
312
+ arg.split('')[1..-1].each do |chr|
313
+ if chr == 'd'
314
+ set_debug
315
+ elsif chr == 'v'
316
+ set_verbose
317
+ elsif chr == 'f'
318
+ set_log_fg
319
+ elsif chr == 'r'
320
+ set_reset_ses
321
+ elsif chr == 'a'
322
+ set_autoupdate
323
+ elsif chr == 'S'
324
+ set_say
325
+ else
326
+ invalid_option(arg,chr)
327
+ end
328
+ end
329
+ elsif valid_env?(arg)
330
+ @args[:env_path] = File.expand_path(arg)
331
+ @args[:conf_files].push( File.expand_path( File.join( arg, 'conf', 'config.yaml' ) ) )
332
+ else
333
+ invalid_env( arg )
334
+ end
335
+ end
336
+ end
337
+ if expect_option
338
+ puts "no value for option #{option_name.to_s.inspect}"
339
+ puts "Type 'rsence help #{@cmd.to_s} for usage."
340
+ exit
341
+ end
342
+ end
343
+ if valid_env?(@args[:env_path])
344
+ conf_file = File.expand_path( File.join( @args[:env_path], 'conf', 'config.yaml' ) )
345
+ @args[:conf_files].push( conf_file ) unless @args[:conf_files].include?( conf_file )
346
+ else
347
+ puts "invalid environment."
348
+ exit
349
+ end
350
+ @startable = true
351
+ end
352
+
353
+ def set_debug
354
+ @args[:debug] = true
355
+ @args[:verbose] = true
356
+ @args[:autoupdate] = true
357
+ @args[:client_pkg_quiet] = false
358
+ end
359
+ def set_verbose
360
+ @args[:verbose] = true
361
+ end
362
+ def set_log_fg
363
+ @args[:log_fg] = true
364
+ end
365
+ def set_reset_ses
366
+ @args[:reset_ses] = true
367
+ end
368
+ def set_autoupdate
369
+ @args[:autoupdate] = true
370
+ end
371
+ def set_say
372
+ @args[:say] = true
373
+ end
374
+
375
+ def valid_env?( arg )
376
+ path = File.expand_path( arg )
377
+ if not File.exists?( path )
378
+ puts "no such directory: #{path.inspect}"
379
+ return false
380
+ elsif not File.directory?( path )
381
+ puts "not a directory: #{path.inspect}"
382
+ return false
383
+ end
384
+ conf_path = File.join( path, 'conf' )
385
+ if not File.exists?( conf_path )
386
+ puts "no conf directory, expected: #{conf_path.inspect}"
387
+ return false
388
+ elsif not File.directory?( conf_path )
389
+ puts "not a conf directory, expected: #{conf_path.inspect}"
390
+ return false
391
+ end
392
+ conf_file = File.join( path, 'conf', 'config.yaml' )
393
+ if not File.exists?(conf_file)
394
+ puts "missing conf file, expected: #{conf_file.inspect}"
395
+ return false
396
+ elsif not File.file?( conf_file )
397
+ puts "conf file not a file, expected: #{conf_file.inspect}"
398
+ return false
399
+ end
400
+ plugin_path = File.join( path, 'plugins' )
401
+ if not File.exists?( plugin_path )
402
+ warn "Warning; no plugin directory in project, expected: #{plugin_path.inspect}" if @args[:verbose]
403
+ elsif not File.directory?( plugin_path )
404
+ puts "plugin directory not a directory, expected: #{plugin_path.inspect}"
405
+ return false
406
+ end
407
+ run_path = File.join( path, 'run' )
408
+ unless File.exists?( run_path )
409
+ warn "Warning: no run directory: Creating #{run_path.inspect}" if @args[:verbose]
410
+ Dir.mkdir( run_path )
411
+ end
412
+ log_path = File.join( path, 'log' )
413
+ unless File.exists?( log_path )
414
+ warn "Warning: no log directory: Creating #{log_path.inspect}" if @args[:verbose]
415
+ Dir.mkdir( log_path )
416
+ end
417
+ db_path = File.join( path, 'db' )
418
+ unless File.exists?( db_path )
419
+ warn "Warning: no db directory: Creating #{db_path.inspect}" if @args[:verbose]
420
+ Dir.mkdir( db_path )
421
+ end
422
+ return true
423
+ end
424
+
425
+ def invalid_env( arg )
426
+ puts "invalid environment: #{arg.inspect}"
427
+ puts "Type 'rsence help #{@cmd.to_s}' for usage."
428
+ puts "Type 'rsence help initenv' for environment initialization usage."
429
+ exit
430
+ end
431
+
432
+ def invalid_option(arg,chr=false)
433
+ if not chr
434
+ puts "invalid option: #{arg.inspect}"
435
+ else
436
+ puts "invalid option character #{chr.inspect} in option character block #{arg.inspect}"
437
+ end
438
+ puts "Type 'rsence help #{@cmd.to_s}' for usage."
439
+ exit
440
+ end
441
+
442
+ def test_port( port, addr='127.0.0.1' )
443
+ require 'socket'
444
+ begin
445
+ sock = TCPsocket.open( addr, port )
446
+ sock.close
447
+ return true
448
+ rescue Errno::ECONNREFUSED
449
+ return false
450
+ end
451
+ end
452
+
453
+ def parse_status_argv
454
+ init_args
455
+ expect_option = false
456
+ option_name = false
457
+ if @argv.length >= 2
458
+ @argv[1..-1].each_with_index do |arg,i|
459
+ if expect_option
460
+ if [:port,:latency].include?(option_name) and arg.to_i.to_s != arg
461
+ puts "invalid #{option_nam.to_s}, expected number: #{arg.inspect}"
462
+ puts "Type 'rsence help #{@cmd.to_s}' for usage."
463
+ exit
464
+ elsif option_name == :conf_files
465
+ if not File.exists?( arg ) or not File.file?( arg )
466
+ puts "no such configuration file: #{arg.inspect}"
467
+ puts "Type 'rsence help #{@cmd.to_s}' for usage."
468
+ exit
469
+ else
470
+ @args[:conf_files].push( arg )
471
+ end
472
+ else
473
+ @args[option_name] = arg
474
+ end
475
+ expect_option = false
476
+ else
477
+ if arg.start_with?('--')
478
+ if arg == '--debug'
479
+ set_debug
480
+ elsif arg == '--verbose'
481
+ set_verbose
482
+ elsif arg == '--port'
483
+ expect_option = true
484
+ option_name = :port
485
+ elsif arg == '--addr'
486
+ expect_option = true
487
+ option_name = :addr
488
+ elsif arg == '--server'
489
+ expect_option = true
490
+ option_name = :server
491
+ elsif arg == '--conf' or arg == '--config'
492
+ expect_option = true
493
+ option_name = :conf_files
494
+ else
495
+ invalid_option(arg)
496
+ end
497
+ elsif arg.start_with?('-')
498
+ arg.split('')[1..-1].each do |chr|
499
+ if chr == 'd'
500
+ set_debug
501
+ elsif chr == 'v'
502
+ set_verbose
503
+ else
504
+ invalid_option(arg,chr)
505
+ end
506
+ end
507
+ elsif valid_env?(arg)
508
+ @args[:env_path] = File.expand_path(arg)
509
+ @args[:conf_files].push( File.expand_path( File.join( arg, 'conf', 'config.yaml' ) ) )
510
+ else
511
+ invalid_env( arg )
512
+ end
513
+ end
514
+ end
515
+ if expect_option
516
+ puts "no value for option #{option_name.to_s.inspect}"
517
+ puts "Type 'rsence help #{@cmd.to_s} for usage."
518
+ exit
519
+ end
520
+ end
521
+ if valid_env?(@args[:env_path])
522
+ conf_file = File.expand_path( File.join( @args[:env_path], 'conf', 'config.yaml' ) )
523
+ @args[:conf_files].push( conf_file ) unless @args[:conf_files].include?( conf_file )
524
+ else
525
+ puts "invalid environment."
526
+ exit
527
+ end
528
+ require 'conf/default'
529
+ config = Configuration.new(@args).config
530
+ # require 'daemon/daemon'
531
+ # puts "status: #{HTTPDaemon.daemonize.inspect}"
532
+ port = config[:http_server][:port]
533
+ addr = config[:http_server][:bind_address]
534
+ port_status = test_port( port, addr )
535
+ # port_msg = port_status ? 'responds' : 'does not respond'
536
+ # puts "TCP status: #{addr}:#{port} #{port_msg}" if @args[:verbose]
537
+ if RSence.pid_support?
538
+ pid_fn = config[:daemon][:pid_fn]
539
+ if File.exists?( pid_fn )
540
+ pid = File.read( pid_fn ).to_i
541
+ begin
542
+ pid_status = Process.kill('USR2',pid)
543
+ rescue Errno::ESRCH
544
+ pid_status = false
545
+ end
546
+ # pid_msg = pid_status == false ? 'not running' : 'responds'
547
+ # puts "process id (#{pid}) #{pid_msg}" if @args[:verbose]
548
+ else
549
+ puts "no PID file, unable to check process status" if @args[:verbose]
550
+ pid_status = nil
551
+ end
552
+ else
553
+ puts "no PID support, unable to check process status" if @args[:verbose]
554
+ pid_status = nil
555
+ end
556
+ if port_status
557
+ puts "TCP response from #{addr} port #{port}"
558
+ else
559
+ puts "No TCP response from #{addr} port #{port}."
560
+ end
561
+ if RSence.pid_support?
562
+ if pid_status == nil
563
+ puts "No process id, unable to check process status."
564
+ elsif pid_status == false
565
+ puts "No process running."
566
+ else
567
+ puts "Process id #{pid} responded with signal #{pid_status}."
568
+ end
569
+ end
570
+ end
571
+
572
+ def parse_save_argv
573
+ throw "parse_save_argv not implemented!"
574
+ end
575
+
576
+ def parse_setup_argv
577
+ throw "parse_setup_argv not implemented!"
578
+ end
579
+
580
+ def parse_initenv_argv
581
+ throw "parse_initenv_argv not implemented!"
582
+ end
583
+
584
+ def parse_argv
585
+ if @argv.empty?
586
+ puts @@cmd_help[:help_help]
587
+ exit
588
+ else
589
+ cmd = @argv[0].to_sym
590
+ cmd = :help if [:h, :'-h', :'--help', :'-help'].include? cmd
591
+ end
592
+ if @@cmds.include?(cmd)
593
+ @cmd = cmd
594
+ if cmd == :help
595
+ parse_help_argv
596
+ elsif cmd == :version
597
+ version
598
+ exit
599
+ elsif [:run,:start,:stop,:restart].include? cmd
600
+ parse_startup_argv
601
+ elsif cmd == :status
602
+ parse_status_argv
603
+ elsif cmd == :save
604
+ parse_save_argv
605
+ elsif cmd == :setup
606
+ parse_setup_argv
607
+ elsif cmd == :initenv
608
+ parse_initenv_argv
609
+ end
610
+ else
611
+ puts @@cmd_help[:unknown] + cmd.to_s.inspect
612
+ puts @@cmd_help[:help_help]
613
+ exit
614
+ end
615
+ end
616
+
617
+ def help( cmd )
618
+ cmd.to_sym! if cmd.class != Symbol
619
+ puts @@cmd_help[:head]
620
+ if @@cmd_help.has_key?(cmd)
621
+ puts @@cmd_help[cmd]
622
+ else
623
+ puts @@cmd_help[:help_main]
624
+ end
625
+ puts @@cmd_help[:tail]
626
+ end
627
+
628
+ def version
629
+ puts @@version
630
+ end
631
+
632
+ def cmd
633
+ @cmd
634
+ end
635
+
636
+ def args
637
+ @args
638
+ end
639
+
640
+ end
641
+
642
+ @@argv_parser = ARGVParser.new( ARGV )
643
+
644
+ def self.argv; @@argv_parser; end
645
+ def self.cmd; @@argv_parser.cmd; end
646
+ def self.args; @@argv_parser.args; end
647
+ def self.startable?; @@argv_parser.startable?; end
648
+ def self.startup
649
+ puts "Loading configuration..." if self.args[:verbose]
650
+ # Use the default configuration:
651
+ require 'conf/default'
652
+ @@config = Configuration.new(self.args).config
653
+ def self.config
654
+ @@config
655
+ end
656
+ ## Riassence Daemon controls
657
+ require 'daemon/daemon'
658
+ puts "Starting RSence..." if self.args[:verbose]
659
+ daemon = HTTPDaemon.new
660
+ daemon.daemonize!
661
+ end
662
+
663
+ end
664
+