Gauche-FUSEでOutputzファイルシステム

シェルでの操作も Outputz に入れたいが良い方法はないかな。キーロガーまで行くとやりすぎ感がある。ヒストリを定期的に POST かな。

cd, ls しすぎ問題を把握したい。

http://d.hatena.ne.jp/higepon/20081125/1227598921


  • 使い方

$ mkdir /tmp/fs
$ ./outputz.scm -m="/tmp/fs" -k=**SECRET_KEY**
$ cd /tmp/fs

楽しんだ後で...
$ fusermount -u /tmp/fs

Outputzファイルシステム上では、以下をカウントします。

  • readdirの回数
  • readしたバイト量
  • writeしたバイト量

そして、dfすると、記録したカウントをOutputzに送信します。

$ df /tmp/fs

unique: 114, opcode: STATFS (17), nodeid: 1, insize: 40
200: ok uri=http://teruaki-desktop/write/home/teruaki/c size=42
200: ok uri=http://teruaki-desktop/read/home/teruaki/a size=42
200: ok uri=http://teruaki-desktop/readdir/home/teruaki size=3

それ以外は通常どおり使えます。

(11/28 11:56追記ここから)
実行してみます。

/home/teruaki% mkdir /tmp/fs
/home/teruaki% ./outputz.scm -m=/tmp/fs -k=****
unique: 1, opcode: INIT (26), nodeid: 0, insize: 56
INIT: 7.9
flags=0x0000000b
max_readahead=0x00020000
INIT: 7.8
flags=0x00000001
max_readahead=0x00020000
max_write=0x00020000
unique: 1, error: 0 (Success), outsize: 40
unique: 2, opcode: LOOKUP (1), nodeid: 1, insize: 47
LOOKUP /.Trash
unique: 2, error: -2 (No such file or directory), outsize: 16
unique: 3, opcode: LOOKUP (1), nodeid: 1, insize: 52
LOOKUP /.Trash-1000
unique: 3, error: -2 (No such file or directory), outsize: 16
unique: 4, opcode: LOOKUP (1), nodeid: 1, insize: 47
LOOKUP /.Trash
unique: 4, error: -2 (No such file or directory), outsize: 16
unique: 5, opcode: LOOKUP (1), nodeid: 1, insize: 52
LOOKUP /.Trash-1000
unique: 5, error: -2 (No such file or directory), outsize: 16

FUSEデバッグプリントです。
別窓を開いて/tmp/fs/home/teruakiにアクセスしてみます。

@別窓
/home/teruaki% cd /tmp/fs/home/teruaki
/tmp/fs/home/teruaki% ls

するとFUSEデバッグプリントが進みます。

ACCESS / 01
unique: 6, error: 0 (Success), outsize: 16
unique: 7, opcode: LOOKUP (1), nodeid: 1, insize: 45
LOOKUP /home
NODEID: 2
unique: 7, error: 0 (Success), outsize: 136
unique: 8, opcode: ACCESS (34), nodeid: 2, insize: 48
ACCESS /home 01
unique: 8, error: 0 (Success), outsize: 16
unique: 9, opcode: LOOKUP (1), nodeid: 2, insize: 48
LOOKUP /home/teruaki
NODEID: 3
unique: 9, error: 0 (Success), outsize: 136
unique: 10, opcode: ACCESS (34), nodeid: 3, insize: 48
ACCESS /home/teruaki 01
unique: 10, error: 0 (Success), outsize: 16
(中略)
unique: 36, opcode: READDIR (28), nodeid: 3, insize: 80
unique: 36, error: 0 (Success), outsize: 16
unique: 37, opcode: RELEASEDIR (29), nodeid: 3, insize: 64
unique: 37, error: 0 (Success), outsize: 16

デバッグプリントの最後に、ls したときのREADDIRがあります。
OutputzファイルシステムはREADDIRの回数をカウントしています。
df するとOutputzに送信します。

@別窓
/tmp/fs/home/teruaki% df
Filesystem サイズ 使用 残り 使用% マウント位置
/dev/sda2 8.0G 4.5G 3.1G 60% /
...
gvfs-fuse-daemon 8.0G 4.5G 3.1G 60% /home/teruaki/.gvfs
/dev/fuse 8.0G 4.5G 3.1G 60% /tmp/fs

するとFUSEの方では

unique: 38, opcode: STATFS (17), nodeid: 1, insize: 40
200: ok uri=http://teruaki-desktop/readdir/home/teruaki size=1
unique: 38, error: 0 (Success), outsize: 96

デバッグプリントしていて、送信が200: okで成功しているのがわかります。

(注意)
FUSEをシングルスレッドで動かしているので、ひとつコマンドが刺さると、ほかのコマンドが処理できず、全体が止まったように見えることがあります。
(11/28 11:56追記ここまで)

#!/usr/bin/env gosh
(use rfc.http)
(use rfc.uri)
(use gauche.parseopt)
(use fuse)

(define secret-key "")

(define ht (make-hash-table 'string=?))

(define (ht-inc! key . opt)
  (let ((v (get-optional opt #f)))
    (hash-table-update! ht key (cut + (if v v 1) <>) 0)))

(define (submit-outputz path size)
  (define hostname (sys-gethostname))
  (define (make-uri path)
    (string-join `("http:/" ,hostname ,path) "/"))

  (receive (status header body)
      (http-post "outputz.com"
		 "/api/post"
		 (apply format "uri=~a&size=~d&key=~a" (map uri-encode-string 
							    (list (make-uri path)
								  (x->string size)
								  secret-key))))
    (format #t "~a: ~a uri=~a size=~a\n" status body (make-uri path) size)))

(define (outputz-statfs path)
  (hash-table-for-each ht submit-outputz)
  (hash-table-clear! ht)
  (sys-statvfs path))

(define (outputz-readdir path fuse-info)
  (ht-inc! (string-append "readdir" path))
  (sys-readdir path))

(define (outputz-mknod path mode rdev)
  (define st (make <sys-stat>))
  (sys-stat->mode-set! st mode)
  (case (slot-ref st 'type)
    ('regular
     (close-output-port (open-output-file path)))
    ('fifo
     (sys-mkfifo path mode))
    (else
     ;;please use the "mknod" external command.
     (error <system-error> :errno ENOENT))))

(define (outputz-open path file-info)
  (close-input-port (open-input-file path)))

(define (outputz-read path outp size offset file-info)
  (call-with-input-file path (lambda (in)
			       (port-seek in offset)
			       (rlet1 v (copy-port in outp)
				      (ht-inc! (string-append "read" path) v)))))

(define (outputz-write path inp size offset fuse-info)
  (call-with-output-file path (lambda (out)
				(port-seek out offset)
				(rlet1 v (copy-port inp out)
				       (ht-inc! (string-append "write" path) v)))
			 :if-exists :overwrite))

(define (main args)
  (let-args (cdr args)
      ((key        "k|key=s")
       (mountpoint "m|mount=s" #f)
       . rest)
    (unless mountpoint (print "usage: -k=YOUR_SECRET_KEY -m=MOUNT_POINT") (exit 1))
    (set! secret-key key)
    (start-fuse mountpoint
	      :getattr  sys-lstat
	      :access   sys-access
	      :readlink sys-readlink
	      :mkdir    sys-mkdir
	      :symlink  sys-symlink
	      :unlink   sys-unlink
	      :rmdir    sys-rmdir
	      :rename   sys-rename
	      :link     sys-link
	      :chmod    sys-chmod
	      :chown    sys-chown
	      :truncate sys-truncate
	      :utimens  sys-utime
	      :statfs   outputz-statfs
	      :readdir  outputz-readdir
	      :mknod    outputz-mknod
	      :open     outputz-open
	      :read     outputz-read
	      :write    outputz-write)))