Newlisp and Dzen2

Started by PapoAnaya, April 05, 2012, 07:26:14 PM

Previous topic - Next topic

PapoAnaya

Hi again



It's been a while, but I still use new lisp for various things as time permit. In this case this is more of a study in system or utilities  than anything else.



One piece of software I've been dabbling of late is called dzen2. For more information you can go to http://sites.google.com/site/gotmor/dzen">//http://sites.google.com/site/gotmor/dzen. Dzen2 is a generic information application akin to conky, but it does not contain any intelligence in terms of monitoring. It accepts information from system input and display them in X. It has some auxiliary programs available for use that are used to create bars and menus.



What I did was to write a small monitoring application for dzen in newlisp in which gathers monitoring data and display them into dzen through the use of pipes. The annotated code is here and hopefully it will make sense.



One thing I noticed while writing this even though I've used perl and dabbled with scsh, newlisp is not slouch when it comes of handling output from command line tools in which a lot of output manipulation can be effectively done with fairly little code.  Hopefully it'll be evident in the enclosed program.



#!/opt/home/papo/bin/newlisp
;; Appareance
;; NOTE: Although these can be used, the only one really in use is
;; SEPARATOR.

(define BACKGROUND-COLOR "'#2c2c32'")
(define ALTERNATE-BACKGROUND-COLOR "'#494b4f'")
(define FOREGROUND-COLOR "'grey70'")
(define SEPARATOR "^p(3)^r(3x3)^p(3)")
(define FONT "fixed")


;; DZEN requires to have regular updates. Even though this is not use
;; right now it is set up every 10 seconds.

;; Main loop interval in seconds
(define SLEEP-INTERVAL 1)


;; Signal handler to exit and not to get stuck in debug mode.

(constant 'SIGINT 2)

(define (ctrlC-handler)
  (exit)
)

(signal SIGINT 'ctrlC-handler)


;; This gets the results of df

(define (get-disk)
  (setq df-results (exec "df"))
; The map is used to get the attributes that we want to monitor.
  (map (fn (x)
(let ((y (parse x)))
  (list (nth 0 y) (nth 4 y))
))
; Using rest removes the header in one swoop.
(rest df-results))
)

;; There is a better way by writing using format, but I'm being lazy
;; This executes gdbar and get a graphical representation of a dzen bar.
;;

(define (get-dbar fs per)
; By duing first, we get the string needed to write the bar

   (first (exec  (append "echo " per "| gdbar -l " fs) ))
)

;
; This will iterate from the df results and only
; get the bar to the ones we care about. In this case
; only the ones mounted in cemnt. This can be changed, of course.


(define (get-dbar-results)
  (let ((df-result (get-disk)) )
    (map (fn (x)
  (if (not (nil? (find "cemnt" (first x)) ))
      (get-dbar (last (parse (first x) "/"))  (nth 1 x)) ))
df-result))
)

;;
;; Same for the memory, get the results from free;
;;

(define (get-free)
  (setq free-results (exec "free"))
; We only need the totals that are at the end.
  (parse (last free-results))
 )

;;
;; We get the bar by computing the percentage of
;; Free / Total
;; And get the resultant bar representation.
;;

(define (get-mem-dbar-results)
  (setq free-result (get-free))
  (get-dbar "MEM:"
  (string (round (mul
(div
 (float (nth 2 free-result))
 (float (nth 1 free-result))) 100 ) 0) ) )
)


;
; Main
;

(define (main)

; These pipes are used to communicate the results to the dzen2 process
; This way results are display without annoying blinks.
;

  (map set '(myin bcout) (pipe))
  (map set '(bcin myout) (pipe))

; Call dzen2 in the background, we only need bcin pipe.

  (setq pid (process "dzen2 -bg gray10 -x 200 -p -ta r  " bcin 0))

; Main loop, what it does is
; get the statistics and write them into
; the dzen2 process

  (while (not nil)
(map (fn (x)
   (if (not (nil? (find "sd" x)))
(write myout (format "%s " x))) )
  (get-dbar-results))
(write myout SEPARATOR)
(write myout " ")
(write myout (get-mem-dbar-results))
(write myout " ")
;
; dzen2 provides a gcpu bar.  In linux, you can create a process
; that reads from /proc. But I'm being lazy being program does the work.
; gcpubar works akin as vmstat, you need to get the second one
; to get an actual measure. In this case, there are 5 iterations
; and the last one is selected.

(write myout (last  (exec "gcpubar -i 1 -c 5  ")))
(write-line myout)

; Wait 10 seconds

(sleep 10000)
)
; Probably these should be moved in the signal handler.

  (destroy pid)
)

;Start here.

(main)


Lutz

#1
Great piece! I wish we had more posts like this on this board. One of newLISP's strengths is communicating with the shell and other processes - in this case shell commands and Dzen2 - The whole success of Unix is based on this ability of communicating and Windows can do much of it too.



Some people try to solve every problem in newLISP. That is not the right approach. There might be some other program, language or C-library suited better to solve a specific problem. Most of the time the loss of speed for starting an external process is not significant for the overall task.

PapoAnaya

#2
Hi:



Well, you try to solve a problem with the best tool available. I do have some of the alternate code available but the objective was to parse code from externally called programs.



One thing I forgot was to show a screenshot, with apologies for the pogoplug advertisement.

http://ppl.ug/36KmI1CcORc/">//http://ppl.ug/36KmI1CcORc/



The indicators that are managed by newlisp are in the upper right hand corner.



Luis

PapoAnaya

#3
Ok, the following is a continuation on the code.



The problem that I was having with the original code is that newlisp was not terminating when dzen2 exited. The issue resides in the way xdm collapses the session and was not killing the newlisp process. Even though you may keep the process id to remove the process, there are times that you might want to terminate the newlisp code if the subordinate process is not longer running.  



The following function runs "ps" and returns if a given process is running or not:




(define (process_exists the_pid)
  (setq get_process (exec "ps"))
  (not (for-all
   (fn (x)
     (not (= the_pid (first (parse x))) ))
   get_process) )
)



You might want to note the way "for-all" is used being that it is using negative logic to determine if a process is running. Rather than checking if the process is there, it is checking that the process is not running and then reverse the result prior to exit.



This allows to scan the whole process table and obtain the desired result without having to parse through the whole result list to get it.



This function gets incorporate in the main loop on the previous code as follows:




(while (not nil)
;...
(if (not (process_exists (string pid)) )
    (throw 0))
)
;...
)



The returned PID is a number and the PID that returns from (first (exec "ps")) is a string.  The type conversion can be done in either direction, but I choose to convert to string. Perhaps converting to integer would have been more efficient, but on a 10 seconds loop, there's not much of a difference.