Systemtap examples, DISK IO - 6 Monitoring Changes to File Attributes
背景
例子来自inodewatch2.stp 脚本, 该脚本用以监控指定文件的权限属性更改.
文件通过stat -c '%D %i' 取出DEV major, minor以及inode. 用法与上一片介绍的inodewatch.stp类似.
脚本内容以及注解 :
[root@db-172-16-3-150 network]# cd /usr/share/systemtap/testsuite/systemtap.examples/io
[root@db-172-16-3-150 io]# cat inodewatch2.stp
#!/usr/bin/stap
global ATTR_MODE = 1
probe kernel.function("setattr_copy")!, // ! 表示如果无法匹配到这个事件则继续匹配下一个事件, 但是不报错.
kernel.function("generic_setattr")!, // , 如果匹配到了则不继续匹配下一个事件, 这样主要针对不同的内核版本做兼容.
kernel.function("inode_setattr") {
dev_nr = $inode->i_sb->s_dev // 文件在内核中的表示, 可以通过MKDEV(major, minor)转换.
inode_nr = $inode->i_ino // 文件的inode号
if (dev_nr == MKDEV($1,$2) # major/minor device
&& inode_nr == $3
&& $attr->ia_valid & ATTR_MODE) //$attr->ia_valid & ATTR_MODE表示当前是改变文件属性的操作.
printf ("%s(%d) %s 0x%x/%u %o %d\n",
execname(), pid(), probefunc(), dev_nr, inode_nr, $attr->ia_mode, uid())
}
// 使用$attr->ia_mode,输出更新后的文件属性.
// 这几个事件函数的源码参考本文末尾部分, 通过这个源码可以看出, 我们还可以利用这个函数来跟踪文件的其他变更操作, 例如ctime, atime, mtime的变更以及用户和组的变更.
执行输出举例 :
// 获得文件的dev和inode
[root@db-172-16-3-150 ~]# stat -c '0x%D %i' /root/anaconda-ks.cfg
0x821 1062620
// 跟踪这个文件的权限更改操作
[root@db-172-16-3-150 io]# stap inodewatch2.stp 0x8 0x21 1062620
// 使用以下命令更改/root/anaconda-ks.cfg文件权限属性
[root@db-172-16-3-150 ~]# chmod 555 /root/anaconda-ks.cfg
[root@db-172-16-3-150 ~]# chmod 666 /root/anaconda-ks.cfg
// 可以看到stap的输出如下
chmod(17939) generic_setattr 0x800021/1062620 100555 0
chmod(17941) generic_setattr 0x800021/1062620 100666 0
接下来我修改一下这个stp文件, 把它变成跟踪文件属主的变更.
// 修改后的stp文件
[root@db-172-16-3-150 io]# vi inodewatch2.stp
#!/usr/bin/stap
global ATTR_UID = 2 // 参考头文件fs.h
global ATTR_GID = 4 // 参考头文件fs.h
probe kernel.function("setattr_copy")!,
kernel.function("generic_setattr")!,
kernel.function("inode_setattr") {
dev_nr = $inode->i_sb->s_dev
inode_nr = $inode->i_ino
if (dev_nr == MKDEV($1,$2) # major/minor device
&& inode_nr == $3)
{
if ($attr->ia_valid & ATTR_UID)
printf ("%s(%d) %s 0x%x/%u %d %d\n",
execname(), pid(), probefunc(), dev_nr, inode_nr, $attr->ia_uid, uid())
if ($attr->ia_valid & ATTR_GID)
printf ("%s(%d) %s 0x%x/%u %d %d\n",
execname(), pid(), probefunc(), dev_nr, inode_nr, $attr->ia_gid, uid())
}
}
// 执行这个脚本, 同样跟踪anaconda-ks.cfg文件
[root@db-172-16-3-150 io]# stap inodewatch2.stp 0x8 0x21 1062620
// 修改anaconda-ks.cfg文件的用户和组
[root@db-172-16-3-150 ~]# chown pg93:oinstall anaconda-ks.cfg
[root@db-172-16-3-150 ~]# id pg93
uid=500(pg93) gid=500(pg93) groups=500(pg93),503(oinstall),504(dba)
// 可以看到跟踪到的修改, 如下 :
chown(18453) generic_setattr 0x800021/1062620 500 0
chown(18453) generic_setattr 0x800021/1062620 503 0
本文的几个事件函数的源码:
[root@db-172-16-3-150 io]# stap -L 'kernel.function("setattr_copy")'
[root@db-172-16-3-150 io]# stap -L 'kernel.function("generic_setattr")'
kernel.function("generic_setattr@fs/attr.c:139") $inode:struct inode* $attr:struct iattr const*
[root@db-172-16-3-150 io]# stap -L 'kernel.function("inode_setattr")'
kernel.function("inode_setattr@fs/attr.c:170") $inode:struct inode* $attr:struct iattr*
[root@db-172-16-3-150 io]# less /usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/fs/attr.c
/**
* generic_setattr - copy simple metadata updates into the generic inode
* @inode: the inode to be updated
* @attr: the new attributes
*
* generic_setattr must be called with i_mutex held.
*
* generic_setattr updates the inode's metadata with that specified
* in attr. Noticably missing is inode size update, which is more complex
* as it requires pagecache updates.
*
* The inode is not marked as dirty after this operation. The rationale is
* that for "simple" filesystems, the struct inode is the inode storage.
* The caller is free to mark the inode dirty afterwards if needed.
*/
void generic_setattr(struct inode *inode, const struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
if (ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
if (ia_valid & ATTR_ATIME)
inode->i_atime = timespec_trunc(attr->ia_atime,
inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MTIME)
inode->i_mtime = timespec_trunc(attr->ia_mtime,
inode->i_sb->s_time_gran);
if (ia_valid & ATTR_CTIME)
inode->i_ctime = timespec_trunc(attr->ia_ctime,
inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MODE) {
umode_t mode = attr->ia_mode;
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
mode &= ~S_ISGID;
inode->i_mode = mode;
}
}
EXPORT_SYMBOL(generic_setattr);
/*
* note this function is deprecated, the new truncate sequence should be
* used instead -- see eg. simple_setsize, generic_setattr.
*/
int inode_setattr(struct inode *inode, struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
if (ia_valid & ATTR_SIZE &&
attr->ia_size != i_size_read(inode)) {
int error;
error = vmtruncate(inode, attr->ia_size);
if (error)
return error;
}
generic_setattr(inode, attr);
mark_inode_dirty(inode);
return 0;
}
EXPORT_SYMBOL(inode_setattr);
头文件 :
/usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/include/linux/fs.h
/*
* Attribute flags. These should be or-ed together to figure out what
* has been changed!
*/
#define ATTR_MODE (1 << 0)
#define ATTR_UID (1 << 1)
#define ATTR_GID (1 << 2)
#define ATTR_SIZE (1 << 3)
#define ATTR_ATIME (1 << 4)
#define ATTR_MTIME (1 << 5)
#define ATTR_CTIME (1 << 6)
#define ATTR_ATIME_SET (1 << 7)
#define ATTR_MTIME_SET (1 << 8)
#define ATTR_FORCE (1 << 9) /* Not a change, but a change it */
#define ATTR_ATTR_FLAG (1 << 10)
#define ATTR_KILL_SUID (1 << 11)
#define ATTR_KILL_SGID (1 << 12)
#define ATTR_FILE (1 << 13)
#define ATTR_KILL_PRIV (1 << 14)
#define ATTR_OPEN (1 << 15) /* Truncating from open(O_TRUNC) */
#define ATTR_TIMES_SET (1 << 16)
参考
1. https://sourceware.org/systemtap/SystemTap_Beginners_Guide/mainsect-disk.html
2. https://sourceware.org/systemtap/examples/
3. /usr/share/systemtap/testsuite/systemtap.examples
4. systemtap-testsuite
5. /usr/share/systemtap/testsuite/systemtap.examples/index.txt
6. /usr/share/systemtap/testsuite/systemtap.examples/keyword-index.txt
7. /usr/share/systemtap/tapset