ถึงคุณจักรนันท์ เรื่อง VFS ครับ

รูปภาพของ niis
niis
Rating 2
Posts: 20
Joined: 23-07-2007

เรื่อง log ของ smb นะครับ

ผมได้ลอง VFS แล้วนะครับ แต่ยังงงกับเรื่อง log อยู่ว่ามันเก็บอะไรบ้าง

อันนี้ใน smb.conf นะครับ เอามาแค่บางส่วน

 log level = 0 vfs:10
# log file = /var/log/samba/%m.log
# all log information in one file
 log file = /var/log/samba/smblog.txt

 

[00-Antivirus]
   comment = Public Share Files
   path = /usr/data/00-Antivirus
   vfs objects= audit recycle
   writable = yes
   read list =
   browseable = yes
   guest ok = yes
   create mask = 0777
   directory mask = 0775

ส่วนอันนี้ก็ log file มันครับ

[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #50 (type 50, layer 0)
  Making operation type 50 opaque [module /[Default VFS]/]
  Accepting operation type 50 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #51 (type 51, layer 0)
  Making operation type 51 opaque [module /[Default VFS]/]
  Accepting operation type 51 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #52 (type 52, layer 0)
  Making operation type 52 opaque [module /[Default VFS]/]
  Accepting operation type 52 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #53 (type 53, layer 0)
  Making operation type 53 opaque [module /[Default VFS]/]
  Accepting operation type 53 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #54 (type 54, layer 0)
  Making operation type 54 opaque [module /[Default VFS]/]
  Accepting operation type 54 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #55 (type 55, layer 0)
  Making operation type 55 opaque [module /[Default VFS]/]
  Accepting operation type 55 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #56 (type 56, layer 0)
  Making operation type 56 opaque [module /[Default VFS]/]
  Accepting operation type 56 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #57 (type 57, layer 0)
  Making operation type 57 opaque [module /[Default VFS]/]
  Accepting operation type 57 from module /[Default VFS]/

 

[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #91 (type 91, layer 0)
  Making operation type 91 opaque [module /[Default VFS]/]
  Accepting operation type 91 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #92 (type 92, layer 0)
  Making operation type 92 opaque [module /[Default VFS]/]
  Accepting operation type 92 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #93 (type 93, layer 0)
  Making operation type 93 opaque [module /[Default VFS]/]
  Accepting operation type 93 from module /[Default VFS]/
[2008/06/19 12:39:01, 5] smbd/vfs.c:vfs_init_custom(174)
  Checking operation #94 (type 94, layer 0)
  Making operation type 94 opaque [module /[Default VFS]/]
  Accepting operation type 94 from module /[Default VFS]/
[2008/06/19 12:39:01, 4] smbd/vfs.c:vfs_ChDir(665)
  vfs_ChDir to /usr/data/02-Sale
[2008/06/19 12:39:01, 4] smbd/vfs.c:vfs_ChDir(665)
  vfs_ChDir to /tmp
[2008/06/19 12:39:01, 4] smbd/vfs.c:vfs_ChDir(665)
  vfs_ChDir to /usr/data/02-Sale
[2008/06/19 12:39:01, 4] smbd/vfs.c:vfs_ChDir(665)
  vfs_ChDir to /tmp
[2008/06/19 12:39:01, 4] smbd/vfs.c:vfs_ChDir(665)
  vfs_ChDir to /usr/data/02-Sale
[2008/06/19 12:39:01, 4] smbd/vfs.c:vfs_ChDir(665)
  vfs_ChDir to /tmp
[2008/06/19 12:39:01, 4] smbd/vfs.c:vfs_ChDir(665)
  vfs_ChDir to /usr/data/02-Sale
[2008/06/19 12:39:06, 10] smbd/vfs.c:vfs_set_filelen(515)
  vfs_set_filelen: ftruncate 202-tc/4BA9E000 to len 512
[2008/06/19 12:39:06, 10] smbd/vfs.c:vfs_allocate_file_space(453)
  vfs_allocate_file_space: file 202-tc/4BA9E000, len 1048576
[2008/06/19 12:39:06, 10] smbd/vfs.c:vfs_set_filelen(515)
  vfs_set_filelen: ftruncate 202-tc/4BA9E000 to len 9216
[2008/06/19 12:39:06, 10] smbd/vfs.c:vfs_allocate_file_space(453)
  vfs_allocate_file_space: file 202-tc/4BA9E000, len 1048576
[2008/06/19 12:39:06, 10] smbd/vfs.c:vfs_set_filelen(515)
  vfs_set_filelen: ftruncate 202-tc/4BA9E000 to len 13312
[2008/06/19 12:39:06, 10] smbd/vfs.c:vfs_allocate_file_space(453)
  vfs_allocate_file_space: file 202-tc/4BA9E000, len 1048576
[2008/06/19 12:39:06, 10] smbd/vfs.c:vfs_allocate_file_space(453)
  vfs_allocate_file_space: file 202-tc/4BA9E000, len 1048576
[2008/06/19 12:39:06, 10] smbd/vfs.c:vfs_set_filelen(515)
  vfs_set_filelen: ftruncate 202-tc/4BA9E000 to len 17408
[2008/06/19 12:39:06, 10] smbd/vfs.c:vfs_allocate_file_space(453)
  vfs_allocate_file_space: file 202-tc/4BA9E000, len 1048576
[2008/06/19 12:39:07, 4] smbd/vfs.c:vfs_ChDir(665)
  vfs_ChDir to /tmp

ก็เลยยังงงอยู่ครับว่า มันดูตรงไหนครับว่าใครทำอะไรกับไฟล์ไหนครับ

หรือผมยัง set อะไรไม่ถูกหรือเปล่าครับ

ยังไงคงต้องรบกวนคุณจักรนันท์ อีกทีนะครับ

รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
audit VFS Object

เวลาพิมพ์ไม่มากนักนะครับ เอาเท่าที่ได้ก่อนแล้วกันนะครับ

อันดับแรก ขอแก้ไข smb.conf ของคุณก่อนนะครับ

log level = 0 vfs:10

ตรงนี้ไม่ต้องไปกำหนด vfs:10 ดอกครับ มันไว้กำหนดสำหรับ extd_audit.so และ full_audit.so ครับ แต่ไม่ได้มีผลอะไรเลยกับ audit.so อีกอย่างคือ จะทำให้ log เต็มอย่างรวดเร็ว เพราะระดับ 10 นี่มันเท่ากับ Highest debug level เลย จะมี Message ที่เกินกว่าที่คุณจะใช้ออกมาอย่างมากมายซึ่งเอาไว้ใช้สำหรับผู้พัฒนาครับ ส่วนการกำหนดให้ log ไปเขียนลงไฟล์ smblog.txt นั้น โอเคแล้วครับ แต่อย่าลืมเสียเองล่ะ ว่าตนเองระบุให้ Samba เก็บในไฟล์ไหน

ต่อมาก็ vfs object = audit recycle

เข้าใจว่าลอกตัวอย่างมาเลย ที่เห็นก็เท่ากับ Plugin เอา recycle.so เข้าไปด้วย แต่ดันไม่กำหนด Parameter ให้ recycle เลย ส่วน audit ถูกต้องแล้วครับ บรรทัดนี้ของคุณแปลว่า ให้ Plugin audit.so และ recycle.so เข้าไปทำงานด้วยเมื่อบริการแชร์ [00-Anivirus] แต่ที่คุณ Plug เข้าไปโดยไม่ระบุอะไรอีก recycle ไม่ทำงานแน่ๆ เพราะเขาต้องการ Parameter อีกเช่นดังต่อไปนี้...

recycle:exclude = Thumbs.db
recycle:keeptree = yes
recycle:maxsize = 16777216
recycle:directory_mode = 755
recycle:subdir_mode = 755
recycle:versions = no
recycle:repository = .recycled

recycle Object ใช้เพื่อเวลา Client สั่งลบไฟล์ ก็จะทำการย้ายไฟล์ไปยังที่เรากำหนดแทนที่จะลบทิ้งไปจริงๆ กำหนดตามหลังบรรทัด vfs object = นั่นแหละ กำหนดแยกในแต่ละแชร์ไป แต่ละตัวเดาออกไหมครับว่าคืออะไร

exclude น่าจะเดาออก
keeptree ถ้าเป็น no ก็จะเก็บไฟล์เฉพาะตัวไฟล์ ไม่เก็บทั้งโครงสร้าง ถ้าเป็น yes ก็จะเก็บทั้ง Path เหมือนที่ถูกลบเลย
maxsize มีค่าเป็น Byte นะครับ
directory_mode และ subdir_mode ก็คือให้เปลี่ยน Mode ของไดเร็คทอรี่และไฟล์เป็นอะไรหลังจากย้ายไปแล้ว
versions นี่คือ หากไฟล์เดียวกัน ชื่อเดียวกัน ถูกลบซ้ำมา 2 ครั้ง จะเลือกเก็บไฟล์ตาม Stamp time ล่าสุด หรือเก็บตามครั้งที่ลบล่าสุด
repository คือที่ที่เรากำหนดให้เป็นที่เก็บไฟล์ที่ถูกลบ ในที่นี้ใว่แค่ .recycled ก็หมายความว่า ให้เก็บไว้ที่ Share directory นั้นแหละ แต่ไว้ใน Directory ชื่อ .recycled อีกที เราจะกำหนดเป็น /home/recycle หรือจะที่ไหนก็ได้ครับ

จบการอธิบายการใช้ recycle Object ไว้ตรงนี้ ที่เหลือไปหัดเล่นกันเอาเองนะครับ ติดปัญหาแล้วค่อยมาถาม

มาต่อที่ audit Object กันครับ

ที่คุณไปดูน่ะ หลงทางครับ เพราะโดน Log level ที่ไปกำหนดไว้เป็น vfs:10 หลอกเอาครับ ที่คุณเห็นทั้งหมดเป็น Log ของ VFS ครับสำหรับ Debug Object แต่ไม่ใช่ Log ของ Object ครับ นั่นเป็น Log ก่อน Call เข้า Routine ใน Object ครับ ส่วน Log ของ audit.so จริงน่ะ ส่งผ่านให้ syslog เป็นคนเก็บครับ ดังนั้นมันจะไปอยู่ใน Log ของระบบ ซึ่งก็คือใน /var/log/messages นั่นเองครับ ไปดูสิครับ จะร้องอ้อเลย แต่ถ้าเป็น extd_audit และ/หรือ full_audit ล่ะก็ สามารถกำหนดให้เก็บใน Log ของ Samba เพิ่มได้ด้วย (นอกเหนือจาก syslog) ทำให้ผมติงไว้ว่า ระวังเนื้อที่เต็มนะครับ

ก่อนจะร่ายต่อไป จำเป็นต้องร่ายการทำงานและโครงสร้างพอสังเขปของ Samba และ Linux Kernel เสียหน่อยก่อน เพื่อปูพื้นความเข้าใจของผู้อ่าน

โดยปกติ OS ยอดนิยมอย่าง M$ Windows นั้น จะมีลักษณะการจัด Process แบบ Multitask และ Multithread เมื่อ Server application ตัวหนึ่งทำงาน จะทำแบบ Single process เท่านั้น เวลามี Client connect เข้ามา ก็จะให้บริการโดย Allocate ทรัพยากรเครื่อง (Memory และ CPU Utilize time) ให้กับ Object ใน Code ของตนเอง แล้วจัดให้เป็น Thread แยกทำงานไป ถ้าแยก Task ได้ก็แยกต่อไป (การแยก Thread เป็นหน้าที่ที่คนเขียนโปรแกรมทำไว้ใน Code ของตนเอง แต่การแยก Task เป็นของผู้พัฒนา OS ทำไว้ใน Code ของ OS) แต่ทั้งหมดก็ยังอยู่ในรูปของ 1 Process เท่านั้น ยังต้องมี Thread wait ยังต้องการ Access ข้าม Address กัน (ทำให้มี Sharing violet error ใน M$ OS ไงครับ แต่ Linux ไม่มี) จากจุดนี้ ผู้ชำนาญจากฝั่ง M$ OS หากใช้ความเข้าใจในการเก็บ Log ฝั่งนั้นมาตีความฝั่ง Linux ก็จะเกิด "มึน" ขึ้นมา เพราะคิดในแบบ Single process ตลอด เลยเกิด Sharing violet error ในหัวสมองตนเอง (แซวเล่นนะครับ อย่าถือสา)

อ้าว... แล้ว Linux Kernel มันเป็นอย่างไรล่ะ? Linux Process style จะเป็นแบบ Multi-process, Multitask และ Multithread ครับ 3 ชั้นเลย เมื่อมี Client ร้องขอ Connect เข้ามา ผู้เขียนโปรแกรมก็จะขอทรัพยากรระบบ แล้วปล่อย Object นั้นแยกออกไปทำงานเป็นอีก Process หนึ่งไปเลย เหมือนเป็นอีกโปรแกรมทำงานไปเลย ภายใน Object นั้นจะไปเป็นอีกกี่ Task กี่ Thread ก็เรื่องของ Process นั้นๆ ไป ยกตัวอย่างเช่น telnet Server ที่จริง ไม่ได้มีโปรแกรมอะไรไปรออยู่ที่ Port 23 ดอกครับ มีแค่ตัวเดียวแหละที่ Listen มันทุก Port ที่กำหนด คือ xinetd แต่เมื่อมี Connect ที่ Port 23 มา มันก็ไป Execute เจ้า telnet server ขึ้นมา 1 process แล้วให้บริการโต้ตอบ Connection นั้นไป ก็เท่านั้น Process ไหนเกิดล้ม ก็ไม่กระเทือน Process อื่นๆ แม้จะเป็นบริการเดียวกัน นี่เป็นอีกเหตุผลที่ทำให้ Linux จึงแข็งแกร่งกว่า Microsoft Windows มากมายนัก (ขอระบุชื่อเต็มๆ ด้วยความสาใจส่วนตัว) หาก telnet service ของ Windows เจอปัญหาที่ Connection เดียวจนถึงล้มเหลว จะทำให้ Telnet service ทั้งหมดล้มลงตามกันไปทันที เพราะเป็น Process เดียวกันหมดนั่นเอง ทีนี้... ขอให้คุณลองเปิดบริการ Samba แล้วลองให้เครื่องหนึ่ง Connect เข้าไป แล้วสังเกตุนะครับ จะเกิด Process ชื่อ smb เต็มไปหมด ตามจำนวนแชร์ คูณด้วย จำนวน Client ออกมาเท่ากับ จำนวน Connection แต่ละ Process ก็มีหมายเลข pid ของตัวเอง (pid = Process Identification ถ้าจำไม่ผิด) แต่ละ Process ก็ให้บริการไปเป็น 1 Share ของ 1 Client นั่นจนกว่า Client จะ Close Connection ของ Share นั้นไป Kernel จัด Process ลักษณะนี้ตั้งแต่พื้นฐานระบบเลยทีเดียว ดังนั้นบริการอื่นๆ ก็ใช้ความเข้าใจแบบเดียวกันครับ

เอ๊ะ.. แล้วผมเล่ามานี่ มันเกี่ยวกันยังไงล่ะ ดูเหมือนไม่เห็นเข้าเรื่องเสียที...

เข้าไปแล้วครับ อดทนอ่านอีกหน่อย เดี๋ยวจะเข้าใจว่าไม่เห็นจะเข้าเรื่องตรงไหน (ไม่ได้มุ่งแต่จะเสียดสี M$ นะ)

ด้วยเหตุนี้ audit.so จึงถูก Allocate ขึ้นมาทำงานแยกในทุกๆ Connection ไปนั่นเองไงเล่าครับ เข้าเรื่องหรือยังเอ่ย....

ดังนั้น เมื่อคุณไปดู Log ของ Samba เอง คุณจะเจอลักษณะเช่นทำนองนี้อยู่เท่านั้น....

[2008/06/19 21:18:12, 1] smbd/service.c:make_connection_snum(1033)
  a3h (192.168.0.41) connect to service upload initially as user chakr (uid=500, gid=501) (pid 15834)

นั่นเป็นเพราะ Process ของ Samba หลักได้รับการร้องของ Connect เข้ามาจากเครื่องชื่อ A3H (Notebook ผมเอง) ด้วย IP หมายเลข 192.168.0.41 ของ Connect ไปยังแชร์ชื่อ upload และ Login ด้วย User ชื่อ chakr ซึ่ง uid=500, gid=501 (uid, gid ไปหาเอาเองนะครับ ขี้เกียจพิมพ์อธิบาย หาง่ายครับ) และท้ายสุดบรรทัดเห็นอะไรไหมครับ?

ครับ... Samba สร้างและแยก Process ไปบริการ Connection นี้แล้ว และ Process เป็นหมายเลข 15834 นั่นเอง

เริ่มรำไรหรือยังครับ?

เพราะ Log นั้นเป็น Log หลักของ Samba (Master process) จึงบันทึกแค่นั้นครับ ส่วน audit.so ที่ทำงานใน Process ย่อย ผู้เขียน audit.c ไม่ได้เขียนให้แยกไฟล์เก็บ Log ได้ จึงเอาไปโยนใส่ syslog ของระบบทั้งหมด เมื่อไปดู /var/log/messages คุณจึงพบบรรทัดลักษณะอย่างนี้...

Jun 19 21:18:12 bedroom smbd_audit[15834]: connect to service upload by user chakr
Jun 19 21:18:12 bedroom smbd_audit[15834]: opendir ./ 
Jun 19 21:18:12 bedroom smbd_audit[15834]: opendir . 
Jun 19 21:18:28 bedroom smbd_audit[15834]: mkdir New Folder 
Jun 19 21:18:30 bedroom smbd_audit[15834]: rename ./New Folder -> ./test 
Jun 19 21:18:30 bedroom smbd_audit[15834]: open test (fd 30) 
Jun 19 21:18:30 bedroom smbd_audit[15834]: close fd 30 
Jun 19 21:18:35 bedroom smbd_audit[15834]: opendir test 
Jun 19 21:18:35 bedroom smbd_audit[15834]: open test/New Text Document.txt (fd 30) for writing 
Jun 19 21:18:35 bedroom smbd_audit[15834]: chmod test/New Text Document.txt mode 0x1b4 
Jun 19 21:18:35 bedroom smbd_audit[15834]: close fd 30 
Jun 19 21:18:37 bedroom smbd_audit[15834]: opendir test 
Jun 19 21:18:37 bedroom smbd_audit[15834]: rename test/New Text Document.txt -> test/ttt.txt 
Jun 19 21:18:37 bedroom smbd_audit[15834]: opendir test 
Jun 19 21:18:41 bedroom smbd_audit[15834]: close fd 30 
Jun 19 21:18:41 bedroom smbd_audit[15834]: unlink test/ttt.txt 
Jun 19 21:18:41 bedroom smbd_audit[15834]: rmdir test
Jun 19 21:18:41 bedroom smbd_audit[15834]: opendir .
Jun 19 21:18:49 bedroom smbd_audit[15834]: disconnected

สังเกตุเห็นอะไรไหมครับ?

บรรทัดแรก แสดงว่า chakr Connect เข้าไป ตาม Samba Log เห็นไหมครับ หลังจากนั้นก็บอกทุกรายละเอียดการกระทำเลย ว่าทำอะไรบ้าง จะมีอันหนึ่งที่ผู้อ่านอาจเดาไม่ออก ก็คือ unlink test/ttt.txt นั่นก็คือ ลบไฟล์ test/ttt.txt ไปครับ (audit.c ใช้คำว่า unlink ครับ) นอกนั้น ผมรู้ว่าพวกท่านเดาออกกันหมด

ถึงตรงนี้แล้วยังสงสัยสิว่า มันเกี่ยวกับที่ผมพล่ามอธิบายเรื่อง Multi-process ของ Kernel อย่างไรหว่า?

เกี่ยวคือ ถ้าหลายๆ Process ล่ะก็ ใน /var/log/messages จะไม่เรียบง่ายอย่างนี้ครับ เป็นพรื่ดเลย ละลานตา สอดแทรกกันมันไปเลย จากตัวอย่างที่ Post ให้ดูนี่มันเครื่องเดียว Share เดียว Connection เดียวครับ แต่พอหลายๆ Process แล้วจะส่ง Log กันรัวเลยล่ะครับ

นี่แหละครับ สาเหตุที่ต้องอธิบายให้เข้าใจในสถาปัตยกรรมของ Kernel เสียก่อน

อ้าว... แล้วแบบนี้จะทำอย่างไรล่ะ จะรู้ได้อย่างไรว่า *ใคร* เป็นผู้ทำอะไรเมื่อใด ในเมื่อใน /var/log/messages นั้น audit.so ไม่ระบุเลยว่าเป็นใคร ก็แน่ล่ะครับ audit.so ไม่ทราบดอกครับ ว่า Connection ที่ตัวเอง Plug on อยู่นั้น บริการใครอยู่ แต่... สังเกตุไหมครับ audit.so ระบุ pid ของตนเองทุกบรรทัด

เริ่มเห็นทางสว่างอีกแล้วล่ะสิครับ ใช่แล้วครับ ไปเทียบกับ Log ของ Samba เองก็ทราบได้แล้วว่าเป็นใครมาจากไหน

ปัญหายังไม่หมดครับ ในการใช้งานจริง Connection มีมากเหลือเกิน แยกแยะ Log ยากมาก... งั้น... เราก็อย่าแยกแยะด้วยตาตนเองสิครับ มีจุดอ้างอิงให้แล้ว คือ pid เราก็เขียน Script ให้แยกออกมาทุกวัน ตอนเที่ยงคืน โดยพิจารณาจากวันที่ เอาเฉพาะของ 1 วันที่ผ่านมา แยกออกมา Grouping เป็นราย User แล้ว Save ใหม่เป็น Text ให้ดูง่ายๆ แล้วใส่ cron ไว้ เท่านั้นเราก็ได้ Log แบบที่เราต้องการไงครับ ได้ครบทุก event ไม่ตกหล่นและ... ไม่ซ้ำซาก... (เพราะถ้าเอาจาก smbstatus จะได้ event ซ้ำซากด้วย)

เอาล่ะครับ ผมขอทิ้งไว้เท่านี้ ให้ไปหัดปล้ำเป็นการบ้านกันก่อนนะครับ เพราะที่เหลือ อยู่ที่ฝีมือในการเขียน Script แล้วล่ะครับ อีกซักพักจะมาร่าย Script ให้ไปใช้กันเป็นการเฉลยครับ เท่าที่ทราบ ยังไม่มีใครเคยปล่อย Script เพื่อจัดการตรงนี้เผยแพร่เลย สงสัยจะไม่มีใครไปขอ... ผมไม่เชื่อว่ายังไม่มีใครในโลกเขียนขึ้น หรืออาจจะยังไม่มีใครเขียนจริงจัง เพราะดูคร่าวๆ ใน /var/log/messages ก็พอแล้ว การเขียน Script ตรงนี้ ออกจะ "ดุ" อยู่สักหน่อยนะครับ แต่ขอให้พยายามดูก่อน

รูปภาพของ niis
niis
Rating 2
Posts: 20
Joined: 23-07-2007
กระจ่างเลยครับ

ก่อนอื่นต้องขอขอบคุณ คุณจักรนันท์เป็นอย่างสูงเลยครับ ที่เสียสละเวลามาร่ายยาวตอบให้ผม คิดว่าคงจะใช้เวลาพิมพ์ยอู่พอสมควรเหมือนกันนะครับ

 คำอธิบายของคุณจักรนันท์ทำให้ผมกระล่างขึ้นเยอะเลยครับ

วันนี้เลยไปนั่งปล้ำกับมันทั้งวัน เลยพึ่งได้มาตอบเอาตอนนี้

ตอนนี้ผม set ทุกอย่างได้เกือบสมบูรณ์แล้วครับเหลือเพียงแค่รายละเอียดปลีกย่อยเท่านั้น

ผมไม่ได้ลองทำตามที่คุณจักรนันท์แนะนำนะครับ เพราะดูแล้วคงเกินความสามารถแน่ๆ ผมเลยสร้าง script ให้ ทำการ copy /var/log/messages และ smblog  มาตั้งชื่อใหม่ตามวันที่ปัจจุบัน ทุกวัน แล้วก็เอาไปเก็บไว้ในไว้ใน harddisk ตัวอื่นอีกชุดนึงด้วย (Backup ไว้) เสร็จแล้วก็สั่งลบ messages กับ smblog ทิ้งไปซะ  ที่ทำอย่างนี้เพื่อที่จะได้จัดการกับ log ได้ง่าย คือต้องการดูของวันไหนก็ดูได้เลย

ต่อจากนั้นผมก็ลองเขียน php ขึ้นมา เพื่อ query log มาดู โดยสามารถเลือกดูได้ตามวันที่ และเลือกดูได้ว่าจะดูการลบ การย้าย(เปลี่ยนชื่อ) หรือการเปิด files ผมทำส่วนของการดูการลบ กับย้ายเสร็จแล้ว ส่วนการเปิดยังไม่ได้ทำ(เดี๋ยวอันนี้จะมีคำถามเพิ่มเติม)

ส่วนรื่อง recycle ก็ set ให้มันไปเก็บไว้ใน harddisk อีกตัวนึงเช่นกัน

คราวนี้ก็มีสิ่งที่ผมยังสงสัยอยู่อีกนิดหน่อยดังนี้ครับ

1. ผมยามดูใน messages เพื่อจะดูการเปิด file ปรากฏว่ามันมีการแจ้งว่า open เต็มไปหมดเลยครับ เข้าใจว่า เวลาที่เราเข้าไปดูใน folder ไหน มันก็จะแจ้งว่าเรา open file ที่อยู่ ใน folder นั้นทุกตัวเลยครับ คิดว่าน่าจะประมาณเวลาที่เรา browse ดูด้วย windwos มันจำเป็นต้องไปอ่านรายละเอียดของ file นั้นๆด้วย

ทีนี้ ถ้าจะเอาแต่เฉพาะที่มีคำว่า for write มันก็จะมีแค่ file บางอย่างเท่านั้น ที่จำเป็นต้อง lock เวลาที่เราเปิด เช่น พวก MS office ต่างๆ แต่ถ้าเป็นพวกรูป หรืออะไรที่ไม่จำเป็นต้อง lock มันก็จะไม่มี คำว่า for write ขึ้นมา 

คำถามคือคุณจักรนันท์พอจะมี เทคนิกในการดูไปที่ถูกเปิดหรือถูกอ่านมั๊ยครับ

2. ใน recycle:exclude เราใช้ wildcard ได้มั๊ยครับ เพราะมันมี file ชั่วคราวของ word excel ที่จะขึ้นต้นด้วย ~xxxx.doc ถูกเก็บเต็มไปหมดเลยครับ

3. เวลาที่เราลบ folder ที่อยู่ในชั้นแรก ( เช่น เรา share folder ชื่อ upload แล้วใน upload ก็มี folder test อยู่ ซึ่งในนั้นก็มีทั้ง folder ย่อย และ file อื่นๆด้วย หากเราลบ folder test ทิ้งแล้วมันจะไม่เอาไปเก็บใน recycle ให้ครับ

4. ทำไมเวลาเราลบ messages ทิ้งแล้วมันถึงไม่สร้างใหม่ให้ครับ จนกว่าจะ restart ใหม่เท่านั้น แต่ smblog จะสร้างใหม่ให้ทันที ผมเลยต้องสั่ง restart ทุกครั้งที่ลบเสร็จ

 

สุดท้ายนี้ก็ต้องขอขอบคุณคุณจักรนันท์ด้วยใจจริงอีกครั้งครับ

รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
VFS Object

/var/log/messages ลบทิ้งไม่ได้นะครับ เพราะ syslog จะทำการเปิดไฟล์เพื่อเขียน แล้วเปิดคาไว้อย่างนั้นตั้งแต่แรกไปตลอด ถ้าลบไปแล้วจะทำให้ syslog ไม่สามารถบันทึก Log ได้อีก ต้องทำการ Restart syslog service ใหม่ หากต้องการลบ Log ใน messages จริงๆ ควรใช้คำสั่ง echo > /var/log/messages ครับ เพื่อ Clear Log content ในไฟล์ทิ้งเท่านั้น แต่ไม่ใช่ลบไฟล์ไป

ส่วน Log ของ Samba นั้นไม่มีปัญหาแบบนี้เพราะ Samba เปิดไฟล์เพื่อเขียนแล้วก็ปิดไฟล์เป็นครั้งๆ ไปครับ

ตอบคำถาม :

ข้อ 1) คุณเข้าใจถูกแล้วครับ เพราะ Windows เข้าไปอ่าน Content เพื่อตัดสินว่าเป็นไฟล์อะไรเพื่อแสดงผลบน File browser กรณีนี้ถึงเป็น KDE หรือ GNOME ก็เป็นครับ และแน่นอนครับ อย่าว่าแต่ audit.so เลยแม้แต่ตัว Samba เองก็ไม่อาจทราบได้ดอกครับ ว่าที่ Client เปิดไฟล์อยู่นั้น เกิดจากการกระทำโดยตั้งใจของมนุษย์ซึ่งกำลังเป็นผู้ใช้อยู่หน้า Client หรือไม่ ดังนั้นสำหรับกรณี Open เพื่อ Read นั้น ไม่สามารถแยกแยะได้แน่นอน แต่... เราสามารถ Filter ได้ครับ เพราะมีจุดให้สังเกตุอยู่ว่า เมื่อเป็นฝีมือ File browser เปิดผู้เปิดไฟล์ดู เขาจะเปิดทั้งหมด ทุกไฟล์ การกระทำจะซ้ำๆ กันคือ open กับ close สลับกันไปจนตลอดจำนวนไฟล์และในช่วงเวลาเพียงเสี้ยววินาที ตรงนี้เองที่เราสามารถ Filter ได้ครับ เดี๋ยวผมจะให้ดูตัวอย่างต่อไปครับ

ข้อ 2) สามารถใช้ Wildcard ได้ครับ ใส่ได้หลายตัวด้วยครับ เว้นวรรคเอาในแต่ละตัวครับ เช่น Thumbs.db *.bak ~* ?humbs.* เป็นต้น

ข้อ 3) recycle Object ไม่เก็บ Directory เปล่าครับ เจาะจงเก็บเฉพาะไฟล์ครับ ดังนั้นถ้าลบไฟล์แล้ว ในขณะที่เรากำหนด keeptree = yes ด้วย recycle Object ก็จะเก็บไฟล์นั้นไปยังปลายทางโดยสร้าง Directory ตามต้นทางให้เหมือนกันครับ แต่โดยตัว Object เอง ไม่ได้เจาะจงเก็บ Directory เลยครับ

ข้อ 4) ผมอธิบายเหตุผลไปแล้ว แต่จะบอกเพิ่มว่า ไม่ต้องถึงขั้น Restart เครื่องดอกครับ Restart แต่ Service ชื่อ syslog ก็พอ สำหรับตระกูล Fedora (SIS ก็ใช่) ก็สั่ง service syslog restart เท่านั้นแหละครับ

เอาล่ะครับ ผมจะ Post ตัวอย่าง Script ให้ดูเป็น Reply ต่อไปนะครับ เอาไว้ใน Reply เดียวแล้วเดี๋ยวตาลายครับ

รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
yesterdaysmblog.sh
#!/bin/sh

SMBLOGFILE=/var/log/samba/smblog.txt
SYSLOGFILE=/var/log/messages
TODAYSEC=$(date -d "$(date +%Y-%m-%d)" +%s)
YESTERDAYSEC=$(($TODAYSEC - 86400))
CURRENTLOGSEC=$TODAYSEC
REPLOG1=''
REPLOG2=''

function Month_Inv () {

case $1 in
Jan)    return 01;;
Feb)    return 02;;
Mar)    return 03;;
Apr)    return 04;;
May)    return 05;;
Jun)    return 06;;
Jul)    return 07;;
Aug)    return 08;;
Sep)    return 09;;
Oct)    return 10;;
Nov)    return 11;;
*)    return 12;;
esac
}

function Dig_syslog () {

 SYSLOG="$1"
 FROMLOGTIME="$2"
 CLIENT_USER=$3
 CLIENT_SERV=$4
 CLIENT_NAME=$5
 CLIENT_IP=$6
 CLIENT_PID=$7

  if test -f "$SYSLOG"
  then
    echo $FROMLOGTIME $CLIENT_USER $CLIENT_SERV $CLIENT_NAME $CLIENT_IP $CLIENT_PID Connect
    CONNECTTIMESEC=$(date -d "$FROMLOGTIME" +%s)

    cat "$SYSLOG" | while read SLOG
    do
      if [ "$(echo $SLOG | grep "smbd_audit\[$CLIENT_PID]")" != "" ]
      then
    Month_Inv $(echo $SLOG | awk '{print $1}')
    LogMonth=$?
    LogDate=$(echo $SLOG | awk '{print $2}')
    LogYear=$(date +%Y)
    if [ $LogDate -eq 31 ] && [ $LogMonth -eq 12 ]
    then
      LogYear=$(($LogYear - 1))
    fi
    LogTime="$LogYear/$LogMonth/$LogDate $(echo $SLOG | awk '{print $3}')"

    if [ $(date -d "$LogTime" +%s) -ge $CONNECTTIMESEC ]
    then
      ACTIVITY=$(echo $SLOG | sed 's/.*]: //')
      if [ "$ACTIVITY" != "$REPLOG1" ] && [ "$ACTIVITY" != "$REPLOG2" ] && [ "$(echo $ACTIVITY | awk '{print $1}')" != "connect" ]
      then
        echo "           "$(echo $LogTime | awk '{print $2}') $CLIENT_USER $ACTIVITY
      fi

      REPLOG2="$REPLOG1"
      REPLOG1="$ACTIVITY"

      if [ "$(echo $ACTIVITY | awk '{print $1}')" = "disconnected" ]
      then
        break
      fi
    fi
      fi
    done
  fi
}

function Dig_smblog () {

 SMBLOG="$1"

  if test -f "$SMBLOG"
  then
    cat "$SMBLOG" | while read LINE
    do
      if [ "$(echo $LINE | sed 's/\[/& /' | awk '{print $1}')" = "[" ] && [ "$(echo $LINE | sed 's/\[/& /' | awk '{print $2}' | sed 's/\//-/g')" != "" ]
      then
    CURRENTLOGSEC=$(date -d "$(echo $LINE | sed 's/\[/& /' | awk '{print $2}' | sed 's/\//-/g')" +%s)
    CURRENTLOGTIME=$(echo $LINE | sed 's/\[/& /;s/,//' | awk '{print $2" "$3}')
      fi

      if [ $CURRENTLOGSEC -eq $YESTERDAYSEC ] && [ "$(echo $LINE | grep 'connect to service')" != "" ] && [ "$(echo $LINE | grep '(pid')" != "" ]
      then
    Dig_syslog "$SYSLOGFILE.1" "$CURRENTLOGTIME" $(echo $LINE | sed 's/.*initially as user //;s/ .*//') $(echo $LINE | sed 's/.*connect to service //;s/ initially as user.*//') $(echo $LINE | sed 's/connect to service.*//;s/(.*//') $(echo $LINE | sed 's/connect to service.*//;s/.*(//;s/).*//') $(echo $LINE | sed 's/.*(pid //;s/).*//')
    Dig_syslog "$SYSLOGFILE" "$CURRENTLOGTIME" $(echo $LINE | sed 's/.*initially as user //;s/ .*//') $(echo $LINE | sed 's/.*connect to service //;s/ initially as user.*//') $(echo $LINE | sed 's/connect to service.*//;s/(.*//') $(echo $LINE | sed 's/connect to service.*//;s/.*(//;s/).*//') $(echo $LINE | sed 's/.*(pid //;s/).*//')
      fi
    done
  fi
}


Dig_smblog "$SMBLOGFILE.1"
Dig_smblog "$SMBLOGFILE"

exit 0
รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
ตัวอย่างผลที่ได้จาก yesterdaysmblog.sh
2008/06/21 17:28:12 chakr upload a3h 192.168.0.41 15834 Connect
           17:28:12 chakr opendir ./
           17:28:12 chakr opendir .
           17:28:28 chakr chmod_acl New Folder mode 0x1fd failed: Operation not supported
           17:28:28 chakr mkdir New Folder
           17:28:28 chakr opendir ./
           17:28:28 chakr open New Folder (fd 30)
           17:28:28 chakr close fd 30
           17:28:30 chakr opendir ./
           17:28:30 chakr opendir .
           17:28:30 chakr rename ./New Folder -> ./test
           17:28:30 chakr opendir ./
           17:28:30 chakr open test (fd 30)
           17:28:30 chakr close fd 30
           17:28:30 chakr opendir ./
           17:28:30 chakr open test (fd 30)
           17:28:30 chakr close fd 30
           17:28:32 chakr opendir test
           17:28:32 chakr opendir ./
           17:28:35 chakr opendir test
           17:28:35 chakr open test/New Text Document.txt (fd 30) for writing
           17:28:35 chakr chmod_acl test/New Text Document.txt mode 0x1b4 failed: Operation not supported
           17:28:35 chakr chmod test/New Text Document.txt mode 0x1b4
           17:28:35 chakr fchmod_acl test/New Text Document.txt mode 0x1b4 failed: Operation not supported
           17:28:35 chakr opendir test
           17:28:35 chakr close fd 30
           17:28:37 chakr rename test/New Text Document.txt -> test/ttt.txt
           17:28:38 chakr opendir ./
           17:28:38 chakr opendir .
           17:28:39 chakr open test (fd 30)
           17:28:39 chakr close fd 30
           17:28:41 chakr opendir ./
           17:28:41 chakr opendir test
           17:28:41 chakr open test (fd 30)
           17:28:41 chakr close fd 30
           17:28:41 chakr chmod_acl /home/upload.recycled/test mode 0x1ed failed: Operation not supported
           17:28:41 chakr unlink test/ttt.txt
           17:28:41 chakr rmdir test
           17:28:41 chakr opendir .
           17:28:49 chakr disconnected
2008/06/21 17:30:40 chakr upload a3h 192.168.0.41 15834 Connect
           17:30:40 chakr opendir ./
           17:30:40 chakr opendir .
           17:30:41 chakr open . (fd 30)
           17:30:41 chakr close fd 30
           17:31:02 chakr disconnected
รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
คำอธิบาย yesterdaysmblog.sh

วิธีที่คุณไปลบ Log ของระบบและ Samba ทิ้งไปนั้น ไม่ใช่สิ่งที่ดีเลยครับ ยังมีช่องโหว่ให้หล่นหายได้ด้วย เนื่องจาก LogRotate จะทำการ Rotate ไฟล์ Log ทั้งระบบเพื่อป้องกัน Harddisk เต็มอยู่แล้ว ดังนั้นหลังจากที่ Log Rotate ไปแล้ว ชื่อไฟล์จะเปลี่ยนโดยมีนามสกุล .1 (หรือ .2 .3 ฯลฯ) ตามหลังขึ้นมา และ Reset Log file ให้เป็นไฟล์เปล่า ทีนี้ก็จะเกิดการขาดตอนของข้อมูล Log เพราะถูดตัดตอนไปโดยระบบครับ ดังนั้น Script ผมจึงเขียนไว้ให้ป้องกันปัญหานี้ด้วย มาดูกันครับ

เริ่มจาก Main ก่อน จะเห็นได้ว่าผมให้ Function Dig_smblog ทำงาน 2 ครั้ง โดยครั้งแรกให้เปิดจาก Log เก่าก่อน เพื่อในกรณีที่ระบบได้ Rotate Log ไปแล้ว จะได้ไม่หายไป เสร็จแล้วจึงเปิด Log ปัจจุบันต่อไป

ใน Function Dig_smblog จะทำการไล่ที่ละบรรทัด โดยคุณสามารถระบ Samba Log ได้ที่ตัวแปร SMBLOGFILE ที่ต้น Scriptตามที่คุณกำหนดใน smb.conf ฟังก์ชั่นจะไล่ทีละบรรทัด อ้อ.. ก่อนเข้า Main ตอนต้นของ Script จะเห็นว่า ผมได้ทำการเก็บวันที่ปัจจุบันไว้ใน TODAYSEC แล้วในรูปของ Time stamp และกำหนดค่าวันที่เมื่อวานในรูปของ Time stamp ไว้ใน YESTERSEC ซึ่งก็คือ TODAYSEC ลบด้วยจำนวนวินาทีใน 1 วัน นั่นก็คือ 86,400 วินาทีนั่นเอง ว่ากันในฟังก์ชั่นต่อ ฟังก์ชั่นก็จะทำการอ่านบรรทัดไว้ในตัวแปร LINE ทีละบรรทัดถอดค่าวันที่ของ Log บรรทัดนั้นไว้ใน CURRENTLOGSEC ในรูปของ Time stamp และเวลาในรูปแบบ HH:MM:SS ไว้ใน CURRENTLOGTIME จากนั้นก็มาดูว่า CURRENTLOGSEC เป็นเมื่อวานหรือไม่ ซึ่งก็เอาไปเทียบกัตัวแปร YESTERDAYSEC นั่นเอง และดูว่า Samba Log บรรทัดนั้น เป็นการระบุว่ามี Client Connect เข้าไปใช่หรือไม่ ถ้าใช่ ก็ทำการส่งต่อให้ฟังก์ชั่น Dig_syslog ต่อไป โดยให้ขุดค้นจาก Log เก่าที่นามสกุล .1 ก่อนเพื่อป้องกันปัญหา Log ได้ Rotate เหมือนกัน แล้วจึงไปขุดค้นใน Log ปัจจุบันของระบบต่อไป โดยส่ง Parameter ให้ฟังก์ชั่นได้แก่ ไฟล์ที่จะค้น, เวลา Connect เริ่มต้นที่จะค้น, ชื่อ User ที่ Connect, ชื่อ Share ที่ Connect, ชื่อเครื่องที่ Connect, IP ที่ Connect, และ PID ของ smb ที่บริการ Connection นั้น

ต่อไปก็เข้าสู่ Function Dig_syslog เข้ามาถ้ามี Log ไฟล์ก็ทำการแสดงผลก่อนเลยว่า ใคร Connect เข้ามาจากไหน วัน/เวลาอะไร IP อะไร เครื่องชื่ออะไรและ PID อะไร แล้วทำการคำนวนวันเวลาที่ Connect เป็น Time stamp ไว้ใน CONNECTTIMESEC เนื่องจากต้องเริ่มหาจากเวลานั้น เพราะดูแค่ PID ไม่ได้นะครับ PID มีซ้ำได้ในเวลาต่างกันครับ เหมือนรถ TAXI ที่ว่าง ทะเบียนเดียวกันก็รับผู้โดยสารได้หลายคนในเวลาต่างๆ กัน แต่รับได้ทีละคนไงครับ ดังนั้นหากดูแต่ PID คุณก็จะได้ Event ของ smb process ที่ PID นั้นออกมาทั้งหมด ซึ่งไม่ใช่ของ User เดียว Connection เดียว ในช่วงเวลาเดียวครับ มาต่อกันครับ... ทีนี้ก็เริ่มเปิดไฟล์อ่านมาทีละบรรทัด อ่านมาแล้วก็ตรวจสอบเลยว่าบรรทัดนั้นใช่ Log ที่มาจาก audit.so และเป็นของ PID ที่เรากำลังค้นหาอยู่หรือไม่ ด้วยการตรวจว่าบรรทัดนั้นมีประโยคว่า smb_audit[PID] หรือไม่ ถ้าไม่ก็ผ่านบรรทัดนั้นไป ถ้าใช่ ก็ถอดวันเวลาออกมาเพื่อเป็น Time stamp ซึ่ง มีจุดสังเกตุคือ audit จะลงวันเวลาแบบไม่ลงปี ค.ศ. นะครับ เห็นไหมครับ? ผมจึงต้องเขียนให้ถอดออกมาเป็นวันที่ เดือน แล้วเอาปีปัจจุบันของระบบเป็นตัวตั้งไว้ หากวันที่ใน Log เป็น 31 เดือนเป็น 12 ก็ย่อมแน่นอนแล้วว่า นั่นเป็น Log ของวันสิ้นปี ซึ่งปีก็ต้องเป็นปีปัจจุบัน ลบออกไป 1 ปี เพราะ Script เราค้นหา Log ย้อนหลังของเมื่อวานเสมอ เสร็จแล้วก็เก็บไว้ใน LogTime แล้วเปรียบเทียบกับเวลาที่เริ่ม Connect ในรูปของ Time stamp ซึ่งแน่นอนครับ Log ที่เราต้องการมันต้องเริ่มต้นจากวันเวลาที่ Connect ไปแล้วเสมอ หากวันเวลาไม่มากกว่าหรือเท่ากับเวลา Connect ก็หมายความว่านั่นเป็น PID เดียวกัน แต่ไม่ใช่การ Connect ครั้งที่เราต้องการ (TAXI คันเดิม แต่ไม่ใช่ในช่วงเวลาที่รับผู้โดยสารที่เราจับตา) พอไล่จนเจอบรรทัด Connect ที่ต้องการแล้ว ที่นี้ก็แสดงผล Log ออกมานั่นเอง แต่ก่อนจะแสดงผล ก็ต้องตรวจสอบการกระทำซ้ำๆ ของ File browser ของ Client เสียก่อน ดังนั้นทุกบรรทัดที่ผ่านเงื่อนไขการตรวจสอบมาทั้งหมดว่าเป็นของเป้าหมาย จะถูกเกบ Message ไว้ใน REPLOG1 เสมอ และดัน REPLOG1 ไปไว้ใน REPLOG2 ต่อไป ดังนั้น ก่อนจะแสดงผล Log หากตรวจพบว่า การกระทำของ Client เป็นการกระทำซ้ำๆ เดิมใน 2 การกระทำที่แล้ว (open และ close สลับกัน) ก็จะไม่แสดงผลซ้ำอีกนั่นเองครับ นี่ไงครับ การ Filter แบบง่ายๆ ของผม หลังจากนั้นก็จะไล่บรรทัดไปเรื่อยๆ จนกระทั่งพบ disconnect ก็จะทำการ break ออกจาก Loop ไม่ต้องค้นหาต่อไป เพราะถึงมี Event จาก PID นั้นตามหลังมาอีก นั่นย่อมมาจากการ Connect ใหม่แล้วแน่นอนครับ

yesterdaysmblog.sh ของผมยังมีช่องโหว่ที่ทำให้ข้อมูล Log บางช่วงหล่นหายไปได้นะครับ แต่ผมตั้งใจปล่อยคาไว้ เผื่อใครอยากทดสอบฝีมือก็มาแก้ไขต่อยอด Script ผมได้ครับ ผมจะยินดีมากถ้ามีใครมาต่อยอดต่อไป เพราะนั่นย่อมแสดงถึงพัฒนาการของผู้ดูแลระบบในบ้านเรา  ช่องโหว่ที่ผมปล่อยคาไว้เพราะผม "ขี้เกียจ" เขียนตัวอย่างแล้วน่ะครับ ช่องโหว่นั้นคือ หาก Server นั้นให้บริการเครื่องข้ามวันข้ามคืน มี Client เชื่อมต่ออยู่ข้ามเที่ยงคืน ก็จะไม่มี disconnect event อยู่ใน Log เมื่อ Script ไล่ไป นั่นไม่ทำให้ Script มี Bug ใดๆ ครับ เพราะก็จะจบ Function ไปโดยไม่มี disconnect แต่... เมื่อถึงวันถัดไป Event ของ Connection นั้นๆ ไปตลอดจน Disconnect จะไม่ถูกแสดงผลเลย เพราะ Script ของผมเริ่มหาจากตอนที่ Client เริ่ม Connect เข้ามา ไอ้ที่ Connect คาไว้อยู่แล้วจะไม่สนใจ ดังนั้นตรงจุดนี้ย่อมต้องมีการไปปรับปรุง Script เพิ่มเติมหากใครนำไปใช้กับ Server ที่มี Client ข้ามช่วงเวลาเที่ยงคืนด้วย

สำหรับการนำไปใช้นะครับ หากเรียก Script ทำงานเฉยๆ Script จะแสดงผลออกทาง Terminal เลย ก็ต้อง > ไปยังไฟล์ที่ต้องการครับ เอาไปใส่ cron เวลาเที่ยงคืนของทุกวันได้เลยครับ ทีนี้ก็ไม่จำเป็นต้องไปตั้งลบไฟล์ Log ของ Samba และใน syslog อีกต่อไปครับ ผลที่ได้ก็จะได้ Log ของ Samba ล้วนๆ แยกเป็นราย Connection ไปจนจบ Connectionทีละ Connection ครับ ดูง่ายขึ้นเป็นกอง

สามารถนำไปประยุกต์ได้มากกว่านี้อีก เพราะผมทำให้ทุกค่าที่แสดงผลแยกออกเป็นตัวแปรก่อนจะทำการ echo ออกไป ดังนั้นสามารถให้ Query เข้าไปเก็บใน MySQL ก็ได้ด้วย หรือจะเขียนลงไฟล์เป็น html เลยก็ได้ เพื่อให้สามารถเรียกดูผ่าน Apache บน Browser เครื่องอื่นๆ ได้ด้วย อันนี้ก็แล้วแต่ใครประสงค์จะให้เป็นอย่างไรนะครับ

ผมอยากเห็นชมรมคนเขียน Shell Script เกิดขึ้นในประเทศไทยบ้างจริงๆ แหม... ถ้าผมหนุ่มอีกหน่อยนะ...

ผู้ดูแลระบบ Linux ทั้งหลายจะสบายขึ้นอีกเยอะ ถ้าเขียน Script กันเก่งๆ เชื่อไหมครับ ในงานผมเคยเจอ Italian engineer คนหนึ่ง เขียนภาค Conveyor ทั้งระบบของโรงงานด้วย Shell Script ครับ ทั้งระบบ Conveyor แบ่งออกเป็น 7 ส่วนใหญ่ๆ รวมทั้งสิ้นระยะราว 2 กิโลเมตร เข้า/ออกเครื่องจักรในการผลิต/แปรรูปผลิตภัณฑ์ต่างๆ 14 ครั้ง ทั้งหมดนั่นทำงานด้วย Script ล้วนๆ ส่วนของการส่ง/อ่านค่าต่างๆ ให้ PLC เขาเขียนเป็นโปรแกรมเล็กๆ หลายๆ ตัว เรียกใช้ใน Script ทั้งหมด Script มีการแตก process แยก PID ของตัวเองไปตาม Product เป็นรายตัวตลอดตั้งแต่เข้ามาในระบบ Convoy ของเขาจนออกไปยัง Stock สุดท้ายของ Convoy เขา แต่ละ PID ก็ทำงานแบบติดตามเป็นรายชิ้นๆ เมื่อจบถึงสิ้นสุด Convoy เขาก็จบ Process ไป ดังนั้น ณ เวลาหนึ่ง มี Product อยู่ในสานพานการผลิตกี่ชิ้น ในเครื่องนั้นจะมี Process script จำนวนเท่านั้นทำงานอยู่ด้วย ตอนเขียนทดสอบ ก็ปล่อย Product วิ่งเพียงชิ้นเดียว พอใช้งานจริง ก็แตก Process ทันทีที่ Product แต่ละตัววิ่งผ่าน Proximity switch แรกของระบบ

ผมซูฮกเลยครับ

รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
ปรับปรุง yesterdaysmblog.sh

เป็นการปรับปรุง Script ให้มาใช้ Internal command ให้มากที่สุด ลดจำนวนการ pipe ไปยัง grep, sed และ awk ลง ทำให้ไม่ต้องไป Access harddisk เรียกโปรแกรมทั้ง 3 มาทำการกับตัวแปร String ยังให้การทำงานของ Script ทำงานเร็วขึ้นโดยใช้เวลาน้อยลงไปได้ถึง 90% ครับ Script ที่ปรับปรุงแล้วเป็นดังต่อไปนี้

#!/bin/sh

SMBLOGFILE=/var/log/samba/log.smbd
SYSLOGFILE=/var/log/messages
TODAYSEC=$(date -d "$(date +%Y-%m-%d)" +%s)
YESTERDAYSEC=$(($TODAYSEC - 86400))
CURRENTLOGSEC=$TODAYSEC
REPLOG1=''
REPLOG2=''

function Month_Inv () {

case $1 in
Jan)    return 01;;
Feb)    return 02;;
Mar)    return 03;;
Apr)    return 04;;
May)    return 05;;
Jun)    return 06;;
Jul)    return 07;;
Aug)    return 08;;
Sep)    return 09;;
Oct)    return 10;;
Nov)    return 11;;
*)    return 12;;
esac
}

function Dig_syslog () {

 SYSLOG="$1"
 FROMLOGTIME="$2"
 CLIENT_USER=$3
 CLIENT_SERV=$4
 CLIENT_NAME=$5
 CLIENT_IP=$6
 CLIENT_PID=$7

  if test -f "$SYSLOG"
  then
    echo $FROMLOGTIME $CLIENT_USER $CLIENT_SERV $CLIENT_NAME $CLIENT_IP $CLIENT_PID Connect
    CONNECTTIMESEC=$(date -d "$FROMLOGTIME" +%s)
    LogYear=${FROMLOGTIME:0:4}

    cat "$SYSLOG" | while read SLOG
    do
      if [ "${SLOG/smbd_audit\[$CLIENT_PID]//}" != "$SLOG" ]
      then
    Month_Inv ${SLOG%% *}
    LogMonth=$?
    LogDate=${SLOG#* }
    LogDate=${LogDate%% *}
    if [ $LogDate -eq 31 ] && [ $LogMonth -eq 12 ]
    then
      LogYear=$(($LogYear - 1))
    fi
    LogTime=${SLOG#* * }
    LogTime="$LogYear/$LogMonth/$LogDate ${LogTime%% *}"

    if [ $(date -d "$LogTime" +%s) -ge $CONNECTTIMESEC ]
    then
      ACTIVITY=${SLOG#*smbd_audit\[$CLIENT_PID]: }
      if [ "$ACTIVITY" != "$REPLOG1" ] && [ "$ACTIVITY" != "$REPLOG2" ] && [ "${ACTIVITY%% *}" != "connect" ]
      then
        echo "           "${LogTime##* } $CLIENT_USER $ACTIVITY
      fi

      REPLOG2="$REPLOG1"
      REPLOG1="$ACTIVITY"

      if [ "${ACTIVITY%% *}" = "disconnected" ]
      then
        break
      fi
    fi
      fi
    done
  fi
}

function Dig_smblog () {

 SMBLOG="$1"

  if test -f "$SMBLOG"
  then
    cat "$SMBLOG" | while read LINE
    do
      if [ "${LINE:0:1}" = "[" ]
      then
    CURRENTLOGSEC=$(date -d "${LINE:1:10}" +%s)
    CURRENTLOGTIME=${LINE:1:19}
      fi

      if [ $CURRENTLOGSEC -eq $YESTERDAYSEC ] && [ "${LINE/connect to service /}" != "$LINE" ] && [ "${LINE/(pid /}" != "$LINE" ]
      then
    Dig_syslog "$SYSLOGFILE.1" "$CURRENTLOGTIME" $(echo ${LINE#*initially as user } | sed 's/ .*//') $(echo ${LINE% initially as user*} | sed 's/.*connect to service //') ${LINE%% *} $(echo ${LINE%%\) *} | sed 's/.*(//') $(echo ${LINE##* } | sed 's/).*//')
    Dig_syslog "$SYSLOGFILE" "$CURRENTLOGTIME" $(echo ${LINE#*initially as user } | sed 's/ .*//') $(echo ${LINE% initially as user*} | sed 's/.*connect to service //') ${LINE%% *} $(echo ${LINE%%\) *} | sed 's/.*(//') $(echo ${LINE##* } | sed 's/).*//')
      fi
    done
  fi
}


Dig_smblog "$SMBLOGFILE.1"
Dig_smblog "$SMBLOGFILE"

exit 0
 

รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
ปรับปรุงด้วยความติดลม

หลังจากปรับปรุงเพื่อเพิ่มความเร็วในการประมวลผลไปแล้ว ก็นั่งคิดต่อว่าจะ Filter event ซ้ำๆ ออกอย่างไร ให้ดียิ่งกว่าเดิม ของเดิมผมตรวจ Event ซ้ำย้อนหลัง 2 event เท่านั้น คราวนี้เลยปรับใหม่ มีเวลาเข้ามาเกี่ยวข้อง เนื่องจากเงื่อนไขที่ว่า ถ้าเป็น Software ทำการเปิดไฟล์ จะมีการเปิด/ปิดเร็วมาก ทั้งหมดจบในวินาทีเดียวกัน ผมจึงเอาตรงนี้เป็นเงื่อนไขคือเก็บ Event ย้อนหลังภายในวินาทีเดียวกัน หากมีการกระทำซ้ำๆ เดิมภายในวินาทีนั้นกี่จะซ้ำกี่ครั้งก็ตาม จะแสดงผลออกมาเพียง Event เดียวเท่านั้น ก็ทำให้ Report ลดความยาวลงไปได้อีกมากเลยทีเดียว ยิ่งจำนวนไฟล์ใน Share directory มากเท่าไหร่ การ Filter แบบใหม่นี้ยิ่งสามารถลดการแสดงผล Event ซ้ำๆ ได้มากเป็นจำนวนเท่าตัวตามจำนวนไฟล์ใน Share เลยทีเดียวครับ ก็เลยใช้เวลาไปอีก 2 ชั่วโมงครึ่ง นั่งปรับปรุงไปพร้อมทั้งเพิ่ม Parameter ให้สามารถระบุชื่อไฟล์ได้เลยว่าจะให้ไป Save ไว้ที่ไหน หากไม่ระบุก็จะแสดงผลบน Terminal ไป

ปรับปรุงล่าสุดเป็นดังนี้ครับ

#!/bin/sh

SMBLOGFILE=/var/log/samba/log.smbd
SYSLOGFILE=/var/log/messages
OUTFILE="$1"

TODAYSEC=$(date -d "$(date +%Y-%m-%d)" +%s)
YESTERDAYSEC=$((TODAYSEC - 86400))
CURRENTLOGSEC=$TODAYSEC

declare -a REPLOG
REPTIME=
REPLEN=0

function Month_Inv () {

case $1 in
Jan)    return 01;;
Feb)    return 02;;
Mar)    return 03;;
Apr)    return 04;;
May)    return 05;;
Jun)    return 06;;
Jul)    return 07;;
Aug)    return 08;;
Sep)    return 09;;
Oct)    return 10;;
Nov)    return 11;;
*)    return 12;;
esac
}

function Dig_syslog () {

 SYSLOG="$1"
 FROMLOGTIME="$2"
 CLIENT_USER=$3
 CLIENT_SERV=$4
 CLIENT_NAME=$5
 CLIENT_IP=$6
 CLIENT_PID=$7

  if test -f "$SYSLOG"
  then
    if [ "$OUTFILE" = "" ]
    then
      echo $FROMLOGTIME $CLIENT_USER $CLIENT_SERV $CLIENT_NAME $CLIENT_IP $CLIENT_PID Connect
    else
      echo $FROMLOGTIME $CLIENT_USER $CLIENT_SERV $CLIENT_NAME $CLIENT_IP $CLIENT_PID Connect >> "$OUTFILE"
    fi

    CONNECTTIMESEC=$(date -d "$FROMLOGTIME" +%s)
    LogYear=${FROMLOGTIME:0:4}

    cat "$SYSLOG" | while read SLOG
    do
      if [ "${SLOG/smbd_audit\[$CLIENT_PID]//}" != "$SLOG" ]
      then
    Month_Inv ${SLOG%% *}
    LogMonth=$?
    LogDate=${SLOG#* }
    LogDate=${LogDate%% *}
    if [ $LogDate -eq 31 ] && [ $LogMonth -eq 12 ]; then LogYear=$(($LogYear - 1)); fi
    LogTime=${SLOG#* * }
    LogTime="$LogYear/$LogMonth/$LogDate ${LogTime%% *}"

    if [ $(date -d "$LogTime" +%s) -ge $CONNECTTIMESEC ]
    then
      ACTIVITY=${SLOG#*smbd_audit\[$CLIENT_PID]: }
      if [ "${ACTIVITY%% *}" != "connect" ]
      then
        if [ "$LogTime" = "$REPTIME" ]
        then
          for (( i=0;  i<$REPLEN; i++ ))
          do
        if [ "$ACTIVITY" = "${REPLOG[i]}" ]
        then
          ACTIVITY=
          break
        fi
          done

          if [ "$ACTIVITY" != "" ]
          then
        REPLOG[$REPLEN]="$ACTIVITY"
        REPLEN=$((REPLEN + 1))
          fi
        else
          REPTIME="$LogTime"
          REPLOG[0]="$ACTIVITY"
          REPLEN=1
        fi

        if [ "$ACTIVITY" != "" ]
        then
          if [ "$OUTFILE" = "" ]
          then
        echo "           "${LogTime##* } $CLIENT_USER $ACTIVITY
          else
        echo "           "${LogTime##* } $CLIENT_USER $ACTIVITY >> "$OUTFILE"
          fi
        fi
      fi

      if [ "${ACTIVITY%% *}" = "disconnected" ]; then break; fi
    fi
      fi
    done
  fi
}

function Dig_smblog () {

 SMBLOG="$1"

  if test -f "$SMBLOG"
  then
    cat "$SMBLOG" | while read LINE
    do
      if [ "${LINE:0:1}" = "[" ]
      then
    CURRENTLOGSEC=$(date -d "${LINE:1:10}" +%s)
    CURRENTLOGTIME=${LINE:1:19}
      fi

      if [ $CURRENTLOGSEC -eq $YESTERDAYSEC ] && [ "${LINE/connect to service /}" != "$LINE" ] && [ "${LINE/(pid /}" != "$LINE" ]
      then
    REPTIME=
    Dig_syslog "$SYSLOGFILE.1" "$CURRENTLOGTIME" $(echo ${LINE#*initially as user } | sed 's/ .*//') $(echo ${LINE% initially as user*} | sed 's/.*connect to service //') ${LINE%% *} $(echo ${LINE%%\) *} | sed 's/.*(//') $(echo ${LINE##* } | sed 's/).*//')
    Dig_syslog "$SYSLOGFILE" "$CURRENTLOGTIME" $(echo ${LINE#*initially as user } | sed 's/ .*//') $(echo ${LINE% initially as user*} | sed 's/.*connect to service //') ${LINE%% *} $(echo ${LINE%%\) *} | sed 's/.*(//') $(echo ${LINE##* } | sed 's/).*//')
      fi
    done
  fi
}


Dig_smblog "$SMBLOGFILE.1"
Dig_smblog "$SMBLOGFILE"

exit 0
 

รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
ช่องโหว่ที่ผมยังทิ้งไว้ (ขี้เกียจ)

หากใครจะนำ Script ผมไปใช้งานจริง แล้วไม่อยากตกหล่น Event ตอนข้ามเที่ยงคืนโดยที่ไม่สามารถเพิ่มเติม/แก้ไข Script เองได้ ผมขอแนะนำให้ตั้ง cron ให้ Script ทำงานในเวลา 23:59 ของทุกวันนะครับ จะทำให้ Script สร้าง Report ตั้งแต่เมื่อวานไล่ยาวจนข้ามเที่ยงคืนไปถึงวันนี้เวลา 23:59 ซึ่งทำให้โอกาสตกหล่นลดลงไปได้มาก เว้นเสียแต่มี Client ที่ Connect นานกว่า 47 ชั่วโมง 59 นาทีไปอีก อย่างไรก็ตาม วิธีนี้ทำให้ Report จะออกช้ากว่าเดิม 1 วันครับ คือ Reu Script เวลา 23:59 วันนี้ พอดูพรุ่งนี้เช้า จะได้ Report ของเมื่อวานนี้เท่านั้นครับ

งงไหมครับเนี่ย? 

รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
samba, recycle vfs และ Log 90 วัน

วันนี้ได้ปรับแต่ง SAMBA อีกแล้ว กับเรื่องที่ต้องทำตาม พรบ. ลูกค้าเกิดคำถามว่า ถ้าจะเก็บไฟล์ที่ถูกลบใน SAMBA ซึ่งเดิมก็ใช้ recycle vfs object อยู่แล้ว แต่คราวนี้จะเก็บแยกเป็นวันๆ และเก็บแค่ 90 วันย้อนหลังโดยผู้ดูแลระบบไม่ต้อง Manual ลบเองทีหลัง

ผมเลยลองปล้ำๆ ดู... สรุปว่า... เขียน Shell script ที่จะ Rotate ที่เก็บและแก้ไฟล์ smb.conf ตรง  recycle:repository และดูแลลบ Directory ที่เก่ากว่า 90 วัน จับใส่ cron ไว้ทำงานหลังเที่ยงคืน..

จบครับ แค่นั้น... ใครจะทำก็ลองไปปล้ำดูนะครับ ไม่ยากๆ เรื่องของเรื่องมันอยู่ที่แค่ Idea เท่านั้น ครับ

รูปภาพของ ohno
ohno
Rating 10
Posts: 1209
Joined: 12-05-2003
เพราะเจ้า
เพราะเจ้า นี่ขนาดมีเวลาน้อยนะเนี่ย หุหุหุ ^-^
รูปภาพของ KT
KT
Rating 1
Posts: 4
Joined: 28-03-2007
15: declare: not

15: declare: not found
Syntax error: "(" unexpected

ลอง run script แล้วมันขึ้น error แบบนี้ครับ ต้องแก้ยังไงครับ

รูปภาพของ จักรนันท์
จักรนันท์
Rating 7
Posts: 551
Joined: 10-12-2004
declare เป็น

declare เป็น Internal command ของ bash นะครับ หมายถึงให้ประกาศตัวแปรตามที่กำหนด (-a ก็คือ array)

น่าจะเป็นเพราะ Default shell ไม่ใช่ bash อยู่ ให้ลองแก้ Script บรรทัดแรกจาก

#!/bin/sh

ไปเป็น

#!/bin/bash

แล้วลองดูใหม่ครับ อย่างนี้น่าจะไม่มีปัญหาอะไรแล้ว

รูปภาพของ KT
KT
Rating 1
Posts: 4
Joined: 28-03-2007
ได้แล้วคร
ได้แล้วครับ ขอบคุณมากครับ
รูปภาพของ KT
KT
Rating 1
Posts: 4
Joined: 28-03-2007
ขอถามต่ออ

ขอถามต่ออีกนิดครับ

ตอนนี้ ใน smb.conf ตั้งค่า unix charset = tis-620 ทำให้ใช้ script แล้วชื่อไฟล์อ่านไม่ออก

มีทางหรือเปล่าครับที่จะเปลี่ยนเป็น utf8

เพราะลองเปลี่ยนเองแล้ว ทำให้ชื่อไฟล์ที่ share มาอันอ่านไม่ออกครับ

Syndicate content