mod_dbd_mysql

/configure \                             
--prefix=/usr/local/httpd-2.2.11 \
--enable-modules=most \
--enable-mods-shared=most \
--with-mysql=/opt/local/mysql5 \
--with-included-apr \
--enable-dbd=shared

落とし穴

  • MySQLクライアントがスレッドセーフ(libmysqlclient_r)である必要がる。
    • ./configure --enable-thread-safe-client
    • スレッドセーフでない?libmysqlclientだとリンクしてくれないのかな(MPMがworkerでなければリンクするのかもしれない)

mlockall()呼び出して、メモリ空間をロック、ページアウトしないようにする

http://www.linux.or.jp/JM/html/LDP_man-pages/man2/mlock.2.html

#!/usr/bin/perl

use strict;
use warnings;
use Inline C => 'DATA';

c_mlockall();
sleep 100;

__END__
__C__
#include <sys/mman.h>

int c_mlockall()
{
    return mlockall(MCL_CURRENT|MCL_FUTURE);
}

バックグラウントで実行して。ページ獲得するのに時間かかるみたいだから少し置いてから ps aux

root      1311  2.2  1.6   8304  8304 pts/0    SL   14:59   0:00          \_ perl test.pl
  • VSS == RSS になってる
  • SL ( Sleep + ページロック ) のフラグがたってる

todo

あんまり評判よくないのは何なのかを追って見る

sys_chroot

プロセス単位で有効

  • current->fs->root, current->fs->rootmntを指定したディレクトリのもので書き変えるだけ
    • chrootしたプロセスからは rootよりも上位のファイルは見えない( rootより上位のパスを指定してもカーネルはパスを解決しない? user_walk ?)
    • chrootしてもネットワークやデバイス等のリソース利用には影響しない
 572 
 573 asmlinkage long sys_chroot(const char __user * filename)
 574 {
 575         struct nameidata nd;
 576         int error;
 577         // chrootするディレクトリのnameidataを取得
 578         error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
 579         if (error)
 580                 goto out;
 581         // パーミッション
 582         error = permission(nd.dentry->d_inode,MAY_EXEC,&nd);
 583         if (error)
 584                 goto dput_and_out;
 585 	     // CAP_SYS_CHROOTケーパビリティが必要
 586         error = -EPERM;
 587         if (!capable(CAP_SYS_CHROOT))
 588                 goto dput_and_out;
 589 
 590         // 現在のプロセスのfs(fs_struct)に、chroot先として指定したディレクトリのvfsmount,dentryをセットする
 591         set_fs_root(current->fs, nd.mnt, nd.dentry);
 592 
 593         // altrootmnt//altrootをセットする??? => x86アーキテクチャでは常にNULLなので関係ない
 594         set_fs_altroot();
 595         error = 0;
 596 dput_and_out:
 597         path_release(&nd);
 598 out:
 599         return error;
 600 }


1215 /*
1216  * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
1217  * It can block. Requires the big lock held.
1218  */
1219 void set_fs_root(struct fs_struct *fs, struct vfsmount *mnt,
1220                  struct dentry *dentry)
1221 {
1222         struct dentry *old_root;
1223         struct vfsmount *old_rootmnt;
1224 
1225         // fsは現在のプロセスのfs_struct
1226         write_lock(&fs->lock);
1227         old_root = fs->root;
1228         old_rootmnt = fs->rootmnt;
1229 
1230         // 指定したパスのvfsmountを新しいvfsmountにする
1231         // mntgetはmnt_countをインクリする
1232         fs->rootmnt = mntget(mnt);
1233 
1234         // 指定したパスのdentryをrootとして設定する
1235         // dgetは d_countをインクリする
1236         fs->root = dget(dentry);
1237         write_unlock(&fs->lock);
1238         if (old_root) {
1239                 dput(old_root);
1240                 mntput(old_rootmnt);
1241         }
1242 }

dentryの生成と初期化を追う

ファイルシステムext3

  • どこのレイヤーを処理してるのかを見失わないように
    • dentry
    • inodeオブジェクト
    • super_block
    • ext3_*
struct inode_operations ext3_dir_inode_operations = {
	/* ... */
	.lookup		= ext3_lookup,
	/* ... */
}
  • lookup ... dentry操作の抽象化
    • iget ... inode操作の抽象化 ... ext3層でinode操作
  • inode->i_op->lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
    • reallookup
      • lookup ---> ext3_lookup
      • iget .. inodeオブジェクト割当、super_blockのread_inodeでディスクinode情報で初期化
        • iget_locked ...
            • get_new_inode_fast
              • alloc_inode
          • ifind_fast ... スピンロック,ディスクの待ち合わせ?
            • find_inode_fast ... inodeオブジェクトテーブルの探索
  • ext3_read_inode ... ディスクinodeの情報を読み取り、inodeオブジェクトにセットする
  • [fs/namei.c] real_lookup
/*
 * This is called when everything else fails, and we actually have
 * to go to the low-level filesystem to find out what we should do..
 *
 * We get the directory semaphore, and after getting that we also
 * make sure that nobody added the entry to the dcache in the meantime..
 * SMP-safe
 */
static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
{
	struct dentry * result;
	struct inode *dir = parent->d_inode; // 親dentryのinode .. ディレクトリ

        // セマフォ
	down(&dir->i_sem);
	/*
	 * First re-do the cached lookup just in case it was created
	 * while we waited for the directory semaphore..
	 *
	 * FIXME! This could use version numbering or similar to
	 * avoid unnecessary cache lookups.
	 *
	 * The "dcache_lock" is purely to protect the RCU list walker
	 * from concurrent renames at this point (we mustn't get false
	 * negatives from the RCU list walk here, unlike the optimistic
	 * fast walk).
	 *
	 * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
	 */
	result = d_lookup(parent, name);
	if (!result) {
                // dentryの割当
		struct dentry * dentry = d_alloc(parent, name);
		result = ERR_PTR(-ENOMEM);
		if (dentry) {
                        //inode探索のコストはファイルシステムに依る
                        // fs/ext3/namei.c
			result = dir->i_op->lookup(dir, dentry, nd);
			if (result)
				dput(dentry);
			else
				result = dentry;
		}
		up(&dir->i_sem);
		return result;
	}

	/*
	 * Uhhuh! Nasty case: the cache was re-populated while
	 * we waited on the semaphore. Need to revalidate.
	 */
	up(&dir->i_sem);
	if (result->d_op && result->d_op->d_revalidate) {
		if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) {
			dput(result);
			result = ERR_PTR(-ENOENT);
		}
	}
	return result;
}
// [fs/namei.c] real_lookup -> inode->i_op->lookup 
static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
	struct inode * inode;
	struct ext3_dir_entry_2 * de; // ext3のディレクトリエントリ //新しいバージョン?
	struct buffer_head * bh;

        // dentry->d_name -> struct qstr ( 長さとハッシュを保持しているので 探索、比較が速い文字列として扱える?)
	if (dentry->d_name.len > EXT3_NAME_LEN)
		return ERR_PTR(-ENAMETOOLONG);

	bh = ext3_find_entry(dentry, &de); // ディレクトリエントリの探索 -> bufferから探索
	inode = NULL;
	if (bh) {
               unsigned long ino = le32_to_cpu(de->inode); // inode番号
		brelse (bh);
                // ディレクトリエントリのinode番号からinodeの取得 -> 必要であればディスクinodeから読み込む
		inode = iget(dir->i_sb, ino);

		if (!inode)
			return ERR_PTR(-EACCES);
	}
	if (inode)
            //-> d_add -> d_instantiate -> dentry->d_inode = inode する
            //-> d_rehash -> ハッシュ?
            return d_splice_alias(inode, dentry); 
        
	d_add(dentry, inode);
	return NULL;
}


// dirは親ディレクトリのinodeオブジェクト
// dentryは親ディレクトリのdentry
static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
		inode = iget(dir->i_sb, ino);
}

//iget .. inodeオブジェクト割当、super_blockのread_inodeでディスクinode情報で初期化
1389 static inline struct inode *iget(struct super_block *sb, unsigned long ino)
1390 {
1391         struct inode *inode = iget_locked(sb, ino);
1392 
1393         if (inode && (inode->i_state & I_NEW)) {
		     // inodeオブジェクトの状態がI_NEW ... inodeオブジェクトが割当済み、ディスクのinodeの情報で初期化されてない
1394                 sb->s_op->read_inode(inode); /// ext3_read_inode .. ディスクのinode読み取り、初期化
1395                 unlock_new_inode(inode);
1396         }
1397 
1398         return inode;
1399 }

//// static struct hlist_head *inode_hashtable

 932 struct inode *iget_locked(struct super_block *sb, unsigned long ino)
 933 {
 934         struct hlist_head *head = inode_hashtable + hash(sb, ino);
 935         struct inode *inode;
 936 
 937         inode = ifind_fast(sb, head, ino);
 938         if (inode)
 939                 return inode;
 940         /*
 941          * get_new_inode_fast() will do the right thing, re-trying the search
 942          * in case it had to block at any point.
 943          */
 944         return get_new_inode_fast(sb, head, ino);
 945 }
 
 // ifind_fast ... スピンロック,ディスクの待ち合わせ?
 805 static inline struct inode *ifind_fast(struct super_block *sb,
 806                 struct hlist_head *head, unsigned long ino)
 807 {
 808         struct inode *inode;
 809 
 810         spin_lock(&inode_lock);
 811         inode = find_inode_fast(sb, head, ino);
 812         if (inode) {
 813                 __iget(inode);
 814                 spin_unlock(&inode_lock);
 815                 wait_on_inode(inode);
 816                 return inode;
 817         }
 818         spin_unlock(&inode_lock);
 819         return NULL;
 820 }

 221 /*
 222  * inode_lock must be held
 223  */
 224 void __iget(struct inode * inode)
 225 {
 226         if (atomic_read(&inode->i_count)) {
 227                 atomic_inc(&inode->i_count);
 228                 return;
 229         }
 230         atomic_inc(&inode->i_count);
 231         if (!(inode->i_state & (I_DIRTY|I_LOCK)))
 232                 list_move(&inode->i_list, &inode_in_use);
 233         inodes_stat.nr_unused--;
 234 }

 // find_inode_fast ... inodeオブジェクトテーブルの探索
 524 /*
 525  * find_inode_fast is the fast path version of find_inode, see the comment at
 526  * iget_locked for details.
 527  */
 528 static struct inode * find_inode_fast(struct super_block * sb, struct hlist_head *head, unsigned long ino)
 529 {
 530         struct hlist_node *node;
 531         struct inode * inode = NULL;
 532 
 533 repeat:
             // リスト探索
 534         hlist_for_each (node, head) {
 535                 inode = hlist_entry(node, struct inode, i_hash);
 536                 if (inode->i_ino != ino)
 537                         continue;
 538                 if (inode->i_sb != sb)
 539                         continue;
 540                 if (inode->i_state & (I_FREEING|I_CLEAR)) {
 541                         __wait_on_freeing_inode(inode);
 542                         goto repeat;
 543                 }
 544                 break;
 545         }
 546         return node ? inode : NULL;
 547 }

// caller: iget_locked
/*
 * get_new_inode_fast is the fast path version of get_new_inode, see the
 * comment at iget_locked for details.
 */
static struct inode * get_new_inode_fast(struct super_block *sb, struct hlist_head *head, unsigned long ino)
{
	struct inode * inode;
	// 割当
	inode = alloc_inode(sb);
	if (inode) {
		struct inode * old;

		spin_lock(&inode_lock);
		/* We released the lock, so.. */
		old = find_inode_fast(sb, head, ino);
		if (!old) {
			inode->i_ino = ino;
			inodes_stat.nr_inodes++;
			list_add(&inode->i_list, &inode_in_use);
			hlist_add_head(&inode->i_hash, head);

			// I_NEW ... アロケート済み、初期化されてない
			inode->i_state = I_LOCK|I_NEW;
			spin_unlock(&inode_lock);

			/* Return the locked inode with I_NEW set, the
			 * caller is responsible for filling in the contents
			 */
			return inode;
		}

		/*
		 * Uhhuh, somebody else created the same inode under
		 * us. Use the old inode instead of the one we just
		 * allocated.
		 */
		__iget(old);
		spin_unlock(&inode_lock);
		destroy_inode(inode);
		inode = old;
		wait_on_inode(inode);
	}
	return inode;
}

// inodeオブジェクトの割当
static struct inode *alloc_inode(struct super_block *sb)
{
	static struct address_space_operations empty_aops;
	static struct inode_operations empty_iops;
	static struct file_operations empty_fops;
	struct inode *inode;

	if (sb->s_op->alloc_inode)
		inode = sb->s_op->alloc_inode(sb);
	else
		inode = (struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL);

	if (inode) {

		//inodeの初期化
		//ファイルシステムに依存しないパラメータの初期化
		struct address_space * const mapping = &inode->i_data;

		inode->i_sb = sb;
		inode->i_blkbits = sb->s_blocksize_bits;
		inode->i_flags = 0;
		atomic_set(&inode->i_count, 1);
		inode->i_sock = 0;
		inode->i_op = &empty_iops;
		inode->i_fop = &empty_fops;
		inode->i_nlink = 1;
		atomic_set(&inode->i_writecount, 0);
		inode->i_size = 0;
		inode->i_blocks = 0;
		inode->i_bytes = 0;
		inode->i_generation = 0;
#ifdef CONFIG_QUOTA
		memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
#endif
		inode->i_pipe = NULL;
		inode->i_bdev = NULL;
		inode->i_cdev = NULL;
		inode->i_rdev = 0;
		inode->i_security = NULL;
		inode->dirtied_when = 0;
		if (security_inode_alloc(inode)) {
			if (inode->i_sb->s_op->destroy_inode)
				inode->i_sb->s_op->destroy_inode(inode);
			else
				kmem_cache_free(inode_cachep, (inode));
			return NULL;
		}

		mapping->a_ops = &empty_aops;
 		mapping->host = inode;
		mapping->flags = 0;
		mapping_set_gfp_mask(mapping, GFP_HIGHUSER);
		mapping->assoc_mapping = NULL;
		mapping->backing_dev_info = &default_backing_dev_info;

		/*
		 * If the block_device provides a backing_dev_info for client
		 * inodes then use that.  Otherwise the inode share the bdev's
		 * backing_dev_info.
		 */
		if (sb->s_bdev) {
			struct backing_dev_info *bdi;

			bdi = sb->s_bdev->bd_inode_backing_dev_info;
			if (!bdi)
				bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
			mapping->backing_dev_info = bdi;
		}
		memset(&inode->u, 0, sizeof(inode->u));
		inode->i_mapping = mapping;
	}
	return inode;
}

loadavg_read_proc

ロードアベレージの算出方法

  • runqueue
  • TASK_RUNNING, TASK_UNINTERRUPTIBLE
  • procfs
  • タイマ割り込み
    • ticks , HZ
  63 /*
  64  * These are the constant used to fake the fixed-point load-average
  65  * counting. Some notes:
  // faction .. 分数、割合
  66  *  - 11 bit fractions expand to 22 bits by the multiplies: this gives
  67  *    a load-average precision of 10 bits integer + 11 bits fractional
  68  *  - if you want to count load-averages more often, you need more
  69  *    precision, or rounding will get you. With 2-second counting freq,
  70  *    the EXP_n values would be 1981, 2034 and 2043 if still using only
  71  *    11 bit fractions.
  72  */
  73 extern unsigned long avenrun[];         /* Load averages */
  74 
  75 #define FSHIFT          11              /* nr of bits of precision */
  76 #define FIXED_1         (1<<FSHIFT)     /* 1.0 as fixed-point */
  77 #define LOAD_FREQ       (5*HZ)          /* 5 sec intervals */
  78 #define EXP_1           1884            /* 1/exp(5sec/1min) as fixed-point */
  79 #define EXP_5           2014            /* 1/exp(5sec/5min) */
  80 #define EXP_15          2037            /* 1/exp(5sec/15min) */
  81 
  82 #define CALC_LOAD(load,exp,n) \
  83         load *= exp; \
  84         load += n*(FIXED_1-exp); \
  85         load >>= FSHIFT;


// FSHIFT ... 11?
/* #define LOAD_INT(x) ((x) >> FSHIFT) */
/* #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) */

static int loadavg_read_proc(char *page, char **start, off_t off,
				 int count, int *eof, void *data)
{
	int a, b, c;
	int len;
        // avenrun ... kernel/timer.cのグローバル変数
	a = avenrun[0] + (FIXED_1/200);
	b = avenrun[1] + (FIXED_1/200);
	c = avenrun[2] + (FIXED_1/200);
	len = sprintf(page,"%d.%02d %d.%02d %d.%02d %ld/%d %d\n",
		LOAD_INT(a), LOAD_FRAC(a),
		LOAD_INT(b), LOAD_FRAC(b),
		LOAD_INT(c), LOAD_FRAC(c),
                      // TASK_RUNNINGの数 .. kernel/sched.c
                      // スレッドの数
                      // 最後のプロセスのpid
		nr_running(), nr_threads, last_pid);
	return proc_calc_metrics(page, start, off, count, eof, len);
}

/*
 * nr_running, nr_uninterruptible and nr_context_switches:
 *
 * externally visible scheduler statistics: current number of runnable
 * threads, current number of uninterruptible-sleeping threads, total
 * number of context switches performed since bootup.
 */

// TASK_RUNNING と TASK_UNINTERRUPTIBLE
unsigned long nr_running(void)
{
	unsigned long i, sum = 0;

        // CPUごとのrunqueueを見て、nr_runningを足す
	for_each_online_cpu(i)
          sum += cpu_rq(i)->nr_running; // TASK_RUNNING

	return sum;
}

unsigned long nr_uninterruptible(void)
{
	unsigned long i, sum = 0;

        // CPUごとのrunqueue, TASK_UNINTERRUPTIBLEの数を総計する
	for_each_cpu(i)
          sum += cpu_rq(i)->nr_uninterruptible; // TASK_UNINTERRUPTIBLE

	return sum;
}

/*
 * Nr of active tasks - counted in fixed-point numbers
 */
static unsigned long count_active_tasks(void)
{
    // 全てのCPUのTASK_RUNNING + TASK_UNINTERRUPTIBLE
	return (nr_running() + nr_uninterruptible()) * FIXED_1;
}

/*
 * calc_load - given tick count, update the avenrun load estimates.
 * This is called while holding a write_lock on xtime_lock.
 */
static inline void calc_load(unsigned long ticks)
{
	unsigned long active_tasks; /* fixed-point */
	static int count = LOAD_FREQ;

	count -= ticks;
	if (count < 0) {
		count += LOAD_FREQ;
		active_tasks = count_active_tasks(); // TASK_RUNNING + TASK_UNINTERRUPTIBLE
		CALC_LOAD(avenrun[0], EXP_1, active_tasks);
		CALC_LOAD(avenrun[1], EXP_5, active_tasks);
		CALC_LOAD(avenrun[2], EXP_15, active_tasks);
	}
}

/*
 * Called by the timer interrupt. xtime_lock must already be taken
 * by the timer IRQ!
 */
// タイマ割り込み 1/1000 とか 1/100 秒とかで
static inline void update_times(void)
{
	unsigned long ticks;

	ticks = jiffies - wall_jiffies;
	if (ticks) {
		wall_jiffies += ticks;
		update_wall_time(ticks);
	}
	// ここで計算
	calc_load(ticks);
}

kernel module , seq_file , テンプレ

シェルをテンプレートにするのもナンだけど。

#!/bin/sh

MODULE=mymodule
AUTHOR=hoge

cat <<EOF
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/version.h>

static void *${MODULE}_seq_start(struct seq_file *s, loff_t *pos)
{
    if(*pos >= 1)
        return NULL;

    return pos;
}

static void *${MODULE}_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
    return 0;
}

static void ${MODULE}_seq_stop(struct seq_file *m, void *p)
{

}

static int ${MODULE}_seq_show(struct seq_file *s, void *v)
{
    return 0;
}

static struct seq_operations ${MODULE}_seq_ops = {
    .start = ${MODULE}_seq_start,
    .next  = ${MODULE}_seq_next,
    .stop  = ${MODULE}_seq_stop,
    .show  = ${MODULE}_seq_show
};

static int ${MODULE}_proc_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &${MODULE}_seq_ops);
}

static struct file_operations ${MODULE}_proc_ops = {
    .owner = THIS_MODULE,
    .open  = ${MODULE}_proc_open,
    .read  = seq_read,
    .llseek = seq_lseek,
    .release = seq_release,
};

static int __init ${MODULE}_init(void)
{
    struct proc_dir_entry *pdentry = create_proc_entry("${MODULE}", 0, NULL);
    if(pdentry)
        pdentry->proc_fops = &${MODULE}_proc_ops;
    
    return 0;
}

static void __exit ${MODULE}_exit(void)
{
    remove_proc_entry("${MODULE}", NULL );
}

module_init(${MODULE}_init);
module_exit(${MODULE}_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("${AUTHOR}");
MODULE_DESCRIPTION("");
MODULE_VERSION("1");
MODULE_ALIAS("${MODULE}");
EOF
#!bin/bash

MODULE=mymodule

cat <<EOF

obj-m   := ${MODULE}.o

KDIR    := /lib/modules/\$(shell uname -r)/build
PWD     := \$(shell pwd)

default:
	\$(MAKE) -Wall -Wstrict-prototypes -C \$(KDIR) SUBDIRS=\$(PWD) modules

install: ${MODULE}.o
	insmod ${MODULE}.ko

uninstall: ${MODULE}.o
	rmmod ${MODULE}.ko

EOF

[linux] proftpd

lsで表示 => ユーザー名扱うところまで


// ログインしてls
static int listfile(cmd_rec *cmd, pool *p, const char *name) {

	/* ... */

        if (!opt_n) {

          /* Format nameline using user/group names. */
          snprintf(nameline, sizeof(nameline)-1,
            "%s %3d %-8s %-8s %s %s %2d %s %s", m, (int) st.st_nlink,
            MAP_UID(st.st_uid), MAP_GID(st.st_gid), s,
            months[t->tm_mon], t->tm_mday, timeline, name);

}

// fakeuser ... 見せかけユーザー
#define MAP_UID(x)	(fakeuser ? fakeuser : auth_uid_name(cmd->tmp_pool,(x)))

// uidからユーザー名を決定する( ディスパッチャに投げて決定 ) 
const char *auth_uid_name(pool *p, uid_t uid) {
  cmd_rec *cmd = NULL;
  modret_t *mr = NULL;
  static char namebuf[64];
  char *res = "(?)";

  memset(namebuf, '\0', sizeof(namebuf));

  cmd = make_cmd(p, 1, (void *) uid);

  // uid_name でディスパッチ
  mr = dispatch_auth(cmd, "uid_name");

  if (MODRET_ISHANDLED(mr) && MODRET_HASDATA(mr)) {
    res = mr->data;
    sstrncpy(namebuf, res, sizeof(namebuf));
    res = namebuf;
  }

  if (cmd->tmp_pool) {
    destroy_pool(cmd->tmp_pool);
    cmd->tmp_pool = NULL;
  }

  return res;
}

// モジュールにコマンドをディスパッチする
static modret_t *dispatch_auth(cmd_rec *cmd, char *match) {
  authtable *authtab = NULL;
  modret_t *mr = NULL;

  // ? 
  authtab = pr_stash_get_symbol(PR_SYM_AUTH, match, NULL,
    &cmd->stash_index);

  while (authtab) {
    pr_log_debug(DEBUG6, "dispatching auth request \"%s\" to module mod_%s",
      match, authtab->m->name);

    mr = call_module(authtab->m, authtab->handler, cmd);

    if (MODRET_ISHANDLED(mr) || MODRET_ISERROR(mr))
      break;

    authtab = pr_stash_get_symbol(PR_SYM_AUTH, match, authtab,
      &cmd->stash_index);
  }

  return mr; 
}


void *pr_stash_get_symbol(pr_stash_type_t sym_type, const char *name,
    void *prev, int *idx_cache) {
  int idx;
  struct stash *sym = NULL;

  if (idx_cache && *idx_cache != -1)
    idx = *idx_cache;

  else {

    /* XXX Ugly hack to support mixed cases of directives in config files. */
    if (sym_type != PR_SYM_CONF)
      idx = symtab_hash(name);

    else {
      register unsigned int i;
      char buf[1024];

      memset(buf, '\0', sizeof(buf));
      sstrncpy(buf, name, sizeof(buf)-1);

      for (i = 0; i < strlen(buf); i++)
        buf[i] = tolower((int) buf[i]);

      idx = symtab_hash(buf);
    }

    if (idx_cache)
      *idx_cache = idx;
  }

  if (idx >= PR_TUNABLE_HASH_TABLE_SIZE) {
    if (*idx_cache)
      *idx_cache = -1;

    return NULL;
  }

  if (prev)
    curr_sym = sym = stash_lookup_next(sym_type, name, idx, prev);
  else
    curr_sym = sym = stash_lookup(sym_type, name, idx);

  switch (sym_type) {
    case PR_SYM_CONF:
      return sym ? sym->ptr.sym_conf : NULL;

    case PR_SYM_CMD:
      return sym ? sym->ptr.sym_cmd : NULL;

    case PR_SYM_AUTH:
      return sym ? sym->ptr.sym_auth : NULL;

    case PR_SYM_HOOK:
      return sym ? sym->ptr.sym_hook : NULL;
  }

  /* In case the compiler complains */
  return NULL;
}

// ディスパッチテーブル
static authtable auth_unix_authtab[] = {
  { 0,  "setpwent",	pw_setpwent },
  { 0,  "endpwent",	pw_endpwent },
  { 0,  "setgrent",     pw_setgrent },
  { 0,  "endgrent",	pw_endgrent },
  { 0,	"getpwent",	pw_getpwent },
  { 0,  "getgrent",	pw_getgrent },
  { 0,  "getpwnam",	pw_getpwnam },
  { 0,	"getpwuid",	pw_getpwuid },
  { 0,  "getgrnam",     pw_getgrnam },
  { 0,  "getgrgid",     pw_getgrgid },
  { 0,  "auth",         pw_auth	},
  { 0,  "check",	pw_check },
  { 0,  "uid_name",	pw_uid2name },
  { 0,  "gid_name",	pw_gid2name },
  { 0,  "name_uid",	pw_name2uid },
  { 0,  "name_gid",	pw_name2gid },
  { 0,  "getgroups",	pw_getgroups },
  { 0,  NULL }
};


MODRET pw_uid2name(cmd_rec *cmd) {
  idmap_t *m;
  idauth_t id;
  struct passwd *pw;

  id.uid = (uid_t) cmd->argv[0];
  m = _auth_lookup_id(uid_table, id);

  if (!m->name) {
    /* Wasn't cached, so perform a lookup */

    if (PERSISTENT_PASSWD)
      pw = p_getpwuid(id.uid); // ここ
    else
      pw = getpwuid(id.uid); // ここ

    if (pw) {
      m->name = pstrdup(session.pool ? session.pool : permanent_pool,
        pw->pw_name);
      return mod_create_data(cmd, m->name);
    }

    return DECLINED(cmd);
  }

  return mod_create_data(cmd, m->name);
}

static struct passwd *p_getpwuid(uid_t uid) {
  struct passwd *pw = NULL;

  p_setpwent();
  while ((pw = p_getpwent()) != NULL)
    if (pw->pw_uid == uid)
      break;

  return pw;
}

static void p_setpwent(void) {
  if (pwdf)
    rewind(pwdf);

  else
    if ((pwdf = fopen(PASSWD,"r")) == NULL)
      pr_log_pri(PR_LOG_ERR, "Unable to open password file %s for reading: %s",
        PASSWD, strerror(errno));
}

[c] snippet

  • passwd複製
 728 static struct passwd *passwd_dup(pool *p, struct passwd *pw) {
 729   struct passwd *npw;
 730 
 731   npw = pcalloc(p,sizeof(struct passwd));
 732 
 733   npw->pw_name = pstrdup(p,pw->pw_name);
 734   npw->pw_passwd = pstrdup(p,pw->pw_passwd);
 735   npw->pw_uid = pw->pw_uid;
 736   npw->pw_gid = pw->pw_gid;
 737   npw->pw_gecos = pstrdup(p,pw->pw_gecos);
 738   npw->pw_dir = pstrdup(p,pw->pw_dir);
 739   npw->pw_shell = pstrdup(p,pw->pw_shell);
 740 
 741   return npw;
 742 }