f810d2ec2677e1b8edebd57b2aa31e6830f6c7eb
[spider.git] / html / program.html
1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
2 <html>
3   <head>
4     <title>Programming New Commands</title>
5
6         <meta name="Keywords" content="DX Cluster, DXSpider, Spider, Packet Cluster, DXCluster, Pavillion Software, AK1A, AX25, AX.25, WWV, Packet Radio, Amateur Radio, Propagation, DX, DXing, G1TLH, GB7TLH, Dirk Koopman, Mailing list, Linux, RedHat, PERL">
7         <meta name="Description" content="Software and systems for realtime digital communications between amateur radio stations for the provision of information on propagation conditions and stations operating">
8         <meta name="Author" content="Dirk Koopman G1TLH">
9     <link rel=stylesheet href="style.css" type="text/css" title="default stylesheet">
10
11   </head>
12
13   <body TEXT="#000000" LINK="#0000ff" VLINK="#800080" BGCOLOR="#FFFFFF">
14         <FONT COLOR="#606060"> 
15           <hr>
16           <h2>Programming New Commands</h2>
17           <hr>
18         </font>
19         
20         
21         <address><a href="mailto:djk@tobit.co.uk">Dirk Koopman G1TLH</a></address>
22         <p>
23           <!-- Created: Sun Dec 13 20:25:14 GMT 1998 -->
24           <!-- hhmts start -->
25 Last modified: Mon Dec 28 23:13:21 GMT 1998
26 <!-- hhmts end -->
27         <h4>Introduction</h4>
28         
29         All the commands in the DXSpider system are 'soft', that is they are bits of
30         perl code that are put into specific places in the <tt>/spider</tt> directory tree. 
31         
32         <p>By putting them in a specific place and calling them &lt;command>.pl, they become
33           commands - in real time. Such is the magic of 
34           <a href="http://www.perl.com">perl</a>.
35
36         <h4>Directory Structure</h4>
37
38         The directory structure is very simple:-
39         <table border=2>
40           <tr><td>/spider</td><td>the main directory</td></tr>
41           <tr><td>/spider/data</td><td>where generated and/or reference data goes</td></tr>
42           <tr><td>/spider/data/spots/&lt;year>/&lt;day>.dat</td><td>one day's worth of spots</td></tr>
43           <tr><td>/spider/data/debug/&lt;year>/&lt;day>.dat</td><td>one day's worth of console debugging</td></tr>
44           <tr><td>/spider/data/log/&lt;year>/&lt;month>.dat</td><td>one month's worth of Logging info including things like rcmd, announces, talks etc</td></tr>
45           <tr><td>/spider/data/wwv/&lt;year>/&lt;month>.dat</td><td>one month's worth of WWV</td></tr>
46           <tr><td>/spider/msg</td><td>the messages directory</td></tr>
47           <tr><td>/spider/packclus/files</td><td>the files directory</td></tr>
48           <tr><td>/spider/packclus/bulletin</td><td>the bulletins directory</td></tr>
49           <tr><td>/spider/perl</td><td>where the issued program code lives</td></tr>
50           <tr><td>/spider/local</td><td>where your experimental/site specific programs go</td></tr>
51           <tr><td>/spider/cmd</td><td>where the issued command code lives</td></tr>
52           <tr><td>/spider/local_cmd</td><td>where your experimental command code goes</td></tr>
53         </table>
54
55         <p>A command is put in full as a file under the 'cmd' directory tree, for example, 
56           <tt>announce</tt> lives in <tt>/spider/cmd/announce.pl</tt> and <tt>show/dx</tt> lives
57           in <tt>/spider/cmd/show/dx.pl</tt>. 
58
59         <p>In general terms I don't like the habit of the standard packet cluster software has
60           of taking the DEC VMS command paradigm to the extreme that it has. So I have adopted
61           the convention of separating commands from arguments. So <tt>sh/dx/10 20</tt> is input
62           on the DXSpider system as <tt>sh/dx 10 on 20m</tt>. This is rather contentious.
63
64         <P>In order to maintain a larger level of compatibility, there is an <tt>Aliases</tt> which
65           lives in <tt>/spider/cmd</tt> (or can be overidden by one in <tt>local_cmd</tt>). This file
66           takes standard expressions, parses command lines and produces DXSpider compatible versions
67           of the old Packet Cluster commands. Currently, however, it doesn't do a 100% job because
68           the functionality of the new commands is different (and hopefully better).
69
70         <P>In addition, in the <tt>/spider/perl</tt> directory (overidden by ...) there is 
71           the <tt>Messages</tt> file. This is the file where all the system messages will be stored
72           (because of laziness on my part this isn't currently the case). You will see instances
73           of its use like <tt>$self->msg(&lt;string>&nbsp;[,$arg..])</tt>. This call uses
74           <tt>$self</tt> to determine what language you are in, to return you the correct message.
75           The way arguments are passed to the routine, mean that you can reorder the arguments
76           in your message to suit your language without changing the actual code.
77
78           <p>When you roll your own commands, put
79           your messages in your own copy of the <tt>Messages</tt> file and don't forget
80           to send me the patches for that as well the command itself.
81                             
82         <p>When I issue a new version or patches for an existing version then only files in
83           the <tt>/spider/cmd</tt> and <tt>/spider/perl</tt> directories will normally be altered.
84           Occasionally, one or two of the reference files in <tt>/spider/data</tt> may be altered.
85           The only files likely to be affected are <tt>bands.pl</tt> and <tt>prefix_data.pl</tt>.
86
87         <p>As it says in the next section, <b>PLEASE</b> experiment in the local directories! It will
88           save a lot of pain when patching code. Having said that, if you have been playing, then 
89           remember to remove or rename any files with new releases that claim to have incorporated 
90           your modifications, otherwise <EM>it will continue to use the old ones in your local 
91                 directories!</em>
92
93         <p>If you want to add facilities to the daemon itself or do some
94         fancy local spot routing, you might like to try looking at <a
95         href="local.html">Local</a> customisations.
96
97         <h4>Hints, Tips and Exhortations</h4>
98
99         <ol>
100
101                 <p><li>Every command that can used on the command line lives in either
102                 this directory ('cmd') or in a local version ('local_cmd'). You are
103                 cajoled or ordered not to and generally discouraged from altering the
104                 commands in the 'cmd' directory. You can put local copies in the
105                 'local_cmd' directory and they will override the standard ones.
106
107                 <p><li>If you want to play, do it in the 'local_cmd' directory. It's
108                 very easy and reasonably safe. You can override a command whilst the
109                 cluster is running.  Compilation errors will simply give you error
110                 messages, it won't stop the cluster running - this only happens if you
111                 mess with the internals to the extent that it gets confused...
112                 
113                 <p><li>A command is a piece of perl, it is simply a small snippet of
114                 program that is dynamically loaded into the cluster on invocation from
115                 the command line. The last modification time is used to determine
116                 whether to reload it.
117                 
118                 <p><li>New (or altered) commands are available for test the moment you
119                 save them.
120                 
121                 <p><li>A command is placed into the appropriate directory with a '.pl'
122                 appended to the end. So the 'show/qra' command lives in
123                 'cmd/show/qra.pl' (or a local version would be in
124                 'local_cmd/show/qra.pl'.
125                 
126                 <p><li>For the security conscious, potentially dubious
127                 characters command line args (i.e. not [A-Za-z0-9_/]) are
128                 converted to their hex equivalents. This will almost certainly
129                 mean that the user will get an error message (unless you have
130                 your secret squirrel hat on and have deliberately put such
131                 commands up [in 'local_cmd' of course]).
132
133                 <p><li>The snippets of program you put here are wrapped in an eval { }
134                 and are subroutines derived from the DXChannel class. They effectively
135                 the following declaration :-
136                 <p><pre>
137   sub Emb_&lt;cmdname>($self, $args)
138   {
139      ...
140      your code here
141      ...
142   }
143                 </pre>
144
145                 <p><li>slash characters are replaced by '_' so the equivalent name for
146                 'show/qth' is 'Emb_show_qth'.
147
148                 <p><li>you would normally do a 'my ($self, $line) = @_;' as the first
149                 thing. There are a complete set of accessors for DXUser, DXCommandmode,
150                 DXChannel and most other classes and these are the recommended way of getting at
151                 the contents of these classes.  A fairly standard start might be:-
152                 <p><pre>
153   my ($self, $line) = @_;
154   my @args = split /\s+/, $line;
155   my $call = $self->call;
156   my $user = $self->user;
157   my @out;
158
159   # check privileges
160   return (1, $self->msg('e5')) if $self->priv &lt; 5;
161
162   ....
163   ....
164   some perl code here
165   ....
166   ....
167   return (1, @out);
168                 </pre>
169
170                 <li>$line (in this example) is the rest of the line after the command (as a string).
171
172                 <p><li>You are responsible for maintaining user security. If you have
173                 a command that does something a normal system shouldn't be allowed to
174                 do or see, there is $self->priv (using the above example) which gives
175                 you the running privilege level of the channel. USE IT!
176
177                 <p><li>The privilege levels used in the standard code are:-
178
179                 <p>0 - is the normal user privilege.
180                 <p>1 - is the remote user privilage (you need to be at least 1 to get
181                   any output from an <tt>rcmd</tt>).
182                 <p>5 - is the normal external sysop privilege, give this to commands that
183                   you are prepared to let non-local sysops use.
184                 <p>8 - a <em>very</em> trusted, probably internet rather than radio connected
185                   remote sysop.
186                 <p>9 - the do anything console privilege. 
187                 
188                 <p>The sysop privilege is for things that you are prepared for remote
189                 sysops and clusters to do or see.
190                 
191                 <p>A console privilege can only be executed locally (at least if you have
192                 correctly installed the client program in inetd or ax25d).
193                 
194                 <p>The set/priv command can only be executed by a console privileged 
195                 session.
196
197                 <p><li>You must return a list with a 0 or 1 as the first element. 1
198                 means success and 0 means fail. Each element of the list which follows
199                 is assumed to be one line for output. Don't put \n characters at the
200                 end of an element (the client will put the correct one in if required
201                 [but see below]).
202                 
203                 <p><li><b>DO NOT</b>send output direct to the user unless you <em>really</em>
204                 mean it (i.e. it is never appropriate for this command to be used remotely
205                 as an <tt>rcmd</tt> or from some kind of batch or cron file.
206
207                 <p>What you do instead is create a list using
208                 <pre>
209 my @out;
210         </pre> 
211                 and then <tt>push</tt> stuff onto it. Each element on the list will
212                 become a line of output. For exmaple:-
213                 <pre>
214 #
215 # set a user's password
216 #
217 # Copyright (c) 1998 Iain Phillips G0RDI
218 # 21-Dec-1998
219 #
220 # Syntax:       set/pass &lt;password> &lt;callsign>
221 #
222   
223 my ($self, $line) = @_;
224 my @args = split /\s+/, $line;
225 my $call;
226 my $pass = shift @args;
227 my @out;
228 my $user;
229 my $ref;
230   
231 return (1, $self->msg('e5')) if $self->priv &lt; 9;
232   
233 foreach $call (@args) {
234     $call = uc $call;
235     if ($ref = DXUser->get_current($call)) {
236         $ref->passwd($pass);
237             $ref->put();
238                 push @out, $self->msg("password", $call);
239         } else {
240                 push @out, $self->msg('e3', 'User record for', $call);
241         }
242 }
243 return (1, @out);
244             </pre>
245                 a more complicated example:-
246                 <pre>
247 #
248 # display the band data
249 #
250 # Copyright (c) 1998 - Dirk Koopman G1TLH
251 #
252 # $Id$
253 #
254
255 #$DB::single = 1;
256
257 my ($self, $line) = @_;
258 my @f = split /\s+/, $line;
259 my @bands;
260 my $band;
261 my @out;
262 my $i;
263
264 if (!$line) {
265         @bands = sort { Bands::get($a)->band->[0] &lt;=> Bands::get($b)->band->[0] } Bands::get_keys();
266         push @out, "Bands Available:-";
267         foreach $band (@bands) {
268                 my $ref = Bands::get($band)->band;
269                 my $s = sprintf "%10s: ", $band;
270                 for ($i = 0; $i &lt; $#{$ref}; $i += 2) {
271                         my $from = $ref->[$i];
272                         my $to = $ref->[$i+1];
273                         $s .= ", " if $i;
274                         $s .= "$from -> $to";
275                 }
276                 push @out, $s;
277         } 
278         push @out, "Regions Available:-";
279         @bands = Bands::get_region_keys();
280         foreach $band (@bands) {
281                 my $ref = Bands::get_region($band);
282                 my $s = sprintf("%10s: ", $band ) . join(' ', @{$ref}); 
283                 push @out, $s;
284         }
285 }
286
287 return (1, @out)
288         </pre>
289                 <p><li>As this is perl and it is very easy to alter stuff to get it
290                 correct, I would like to see some intelligent argument processing,
291                 e.g. if you can have one callsign, you can have several. Interpret
292                 your arguments; so for example:-
293
294                 <pre>
295   set/qra jo02lq       - sets your own locator to JO02LQ
296   set/qra g1tlh jo02lq - sets G1TLH's locator (if you are allowed)
297   or
298   show/qra in92jo      - displays the bearing and distance to 
299                          IN92JO using your lat/long or locator
300   show/qra jn56in in92jo  - bearing and distance between two
301                             locators
302         </pre>
303
304                 <p><li>It is important that you remember when you have tie
305                 hashes using MLDBM et al. If you do a
306                 <tt>DXUser->get($call)</tt> you will get a different (older)
307                 thing than the one in <tt>$self->user</tt>. This is almost
308                 certainly NOT what you want if want to modify a user that is
309                 currently connected. Either use <tt>$self->user</tt> or, if
310                 you want another user, use <tt>DXUser->get_current($call)</tt>
311
312                 <p><li>If you want to debug something, start the cluster.pl up thus:-
313                 <pre>
314   perl -d cluster.pl
315   dbg> r
316                 </pre>
317                 Then you can go into debug mode at anytime by using the command :-
318                 <pre> 
319   debug
320         </pre>
321   or you can put the line:-
322                 <pre>
323   $DB::single = 1;
324                 </pre>
325                 in an appropriate place in a command. This will only have an effect
326                 if you are running in perl debug mode.
327                 
328                 <p>If all else fails (actually it is very simple), just stick print
329                   commands in everywhere and the output will appear on the cluster.pl
330                   screen.
331
332                 <p><li>Anything you output with a > as the last character is taken to
333                 mean that this is a prompt and will not have a \r or \n appended to
334                 it in the client for telnet sessions (only).
335
336                 <p><li>help is kept in <tt>/spider/cmd/Command_&lt;lang>.hlp</tt> files.
337                 The format of the help files should be self explanatory, but they are
338                 explained further in the files themselves.
339
340                 <p><li>PLEASE add your new commands to the Commands_*.hlp file so that
341                 people know about and how to use them!
342
343         </ol>
344
345         <h4>Editting the source</h4>
346         
347         I suppose this has to be discussed but although I may have confused some of you, I 
348         insist on the following formatting conventions:-
349
350         <ol>
351                 <p><li>All white space to left of a line shall be tabs.
352                 <p><li>A tab shall be 4 characters (unless it is 8) (I use 4). Anything you see
353                 with multiples of 2 spaces will be reformatted next time I edit it. 
354                 <p><li>You <b><u><i>WILL</i></u></b>use the one true (documented) bracing method as 
355                 documented in K & R and all the 'official' perl books.
356         </ol>
357
358         <p>I have been experimenting with editors and tabwidths and have settled on 
359           <a href="http://www.xemacs.org">XEmacs</a>. You can get a copy from the 
360           <a href="ftp://contrib.redhat.com">RedHat Contrib</a> ftp site for your version
361           of Redhat. I use the following parameters in my .emacs file.
362
363           <pre>
364   ;; End of Options Menu Settings
365   (custom-set-variables
366    '(cperl-electric-parens t)
367    '(cperl-auto-newline t)
368    '(cperl-electric-linefeed t)
369    '(cperl-hairy t)
370    '(tab-width 4)
371    '(cperl-indent-level 4)
372    '(cperl-brace-offset 0)
373    '(cperl-continued-brace-offset -4)
374    '(cperl-label-offset -4)
375    '(cperl-merge-trailing-else nil)
376    '(cperl-continued-statement-offset 4)
377   )
378         </pre>
379
380         I also have all the fancy colouring on (don't know what sets that) but this is 
381         what I have in .xemacs-options file:-
382
383         <pre>
384   ;; -*- Mode: Emacs-Lisp -*-
385
386   (setq options-file-xemacs-version '(20 4))
387     (setq-default case-fold-search t)
388     (setq-default overwrite-mode nil)
389       (setq-default case-fold-search t)
390     (setq-default case-replace t)
391       (setq-default zmacs-regions t)
392     (setq-default mouse-yank-at-point t)
393     (setq-default require-final-newline t)
394     (setq-default next-line-add-newlines nil)
395     (setq-default teach-extended-commands-p t)
396     (setq-default teach-extended-commands-timeout 4)
397     (setq-default debug-on-error nil)
398     (setq-default debug-on-quit nil)
399     (setq-default lpr-switches nil)
400     (setq-default ps-print-color-p t)
401     (setq-default ps-paper-type 'letter)
402     (setq-default get-frame-for-buffer-default-instance-limit nil)
403     (setq-default temp-buffer-show-function 'show-temp-buffer-in-current-frame)
404       (setq-default font-lock-auto-fontify t)
405     (setq-default font-lock-use-fonts nil)
406     (setq-default font-lock-use-colors '(color))
407     (setq-default font-lock-maximum-decoration t)
408     (setq-default font-lock-maximum-size 256000)
409     (setq-default font-lock-mode-enable-list nil)
410     (setq-default font-lock-mode-disable-list nil)
411     (require 'font-lock)
412     (remove-hook 'font-lock-mode-hook 'turn-on-fast-lock)
413     (remove-hook 'font-lock-mode-hook 'turn-on-lazy-shot)
414     (require 'paren)
415     (paren-set-mode 'blink-paren)
416     (if (featurep 'scrollbar) (progn (add-spec-list-to-specifier scrollbar-width 'nil) (add-spe$
417     (add-spec-list-to-specifier modeline-shadow-thickness '((global (nil . 2))))
418     (setq-default truncate-lines nil)
419     (setq-default bar-cursor nil)   
420       (setq-default buffers-menu-max-size 25)
421     (setq-default complex-buffers-menu-p nil)
422     (setq-default buffers-menu-sort-function 'sort-buffers-menu-by-mode-then-alphabetically)
423     (setq-default buffers-menu-grouping-function 'group-buffers-menu-by-mode-then-alphabeticall$
424     (setq-default buffers-menu-submenus-for-groups-p nil)
425     (setq-default font-menu-ignore-scaled-fonts t)
426     (setq-default font-menu-this-frame-only-p nil)
427     (if (featurep 'toolbar) (progn (set-default-toolbar-position 'top) (add-spec-list-to-specif$
428     (setq-default mouse-avoidance-mode nil)
429     (setq-default browse-url-browser-function 'browse-url-w3)
430     </pre>
431
432         I also use <a href="../download/cperl-mode.el.4.19.gz">cperl-mode.el.4.19</a> which I got from
433         <a href="http://www.cpan.org">CPAN</a> for the auto formatting of the perl as I write it. Some
434         of its habits are rather peculiar, but you can either switch them off or learn to live with them
435         as I did. I installed my copy in <b>/usr/lib/xemacs/site-lisp</b>. 
436
437         <p>XEmacs runs perfectly happily on the console as well as under X.
438
439 <!-- Standard Footer!! -->
440         <p>&nbsp;</p>
441         <p>
442           <FONT COLOR="#606060"><hr></font>
443         <font color="#FF0000" size=-2>
444           Copyright &copy; 1998 by Dirk Koopman G1TLH. All Rights Reserved<br>
445         </font>
446         <font color="#000000" size=-2>$Id$</font>
447   </body>
448 </html>