サーバの大掃除のついでに、ユーザ権限でCPANライブラリを導入していたものを一掃したい。そのために、と、Stat::lsMode::format_modeをライブラリを使わずインライン実装する簡易関数をAIに書いてもらった。
Config::TinyのINIファイル読み書きを代替する関数
#!/usr/bin/perl
use Data::Dumper;
$ini_filename = "test.ini";
# *******************
# INI読み込み
my $config = read_ini($ini_filename);
if ( !defined($config) ) {
print("ini file not found\n");
exit(0);
}
my $str_temp = $config->{debug_section}->{debug_attr};
if ( !defined($str_temp) ) {
printf("value not found\n");
}
else {
printf( "[section] attr = %s\n", $str_temp );
}
print("---debug---\n");
print Dumper $config;
print("---debug---\n");
# *******************
# INI書き込み
$time = time();
( $sec, $min, $hour, $mday, $mon, $year, $wday ) = localtime($time);
$new_value = sprintf(
"%04d-%02d-%02d %02d:%02d:%02d",
$year + 1900, $mon + 1, $mday, $hour, $min, $sec
);
# 書き込み
my $config = {};
$config->{debug_section0}->{debug_attr0} = "dummy string 0";
$config->{debug_section}->{debug_attr} = $new_value;
$config->{debug_section}->{debug_attr2} = "dummy string 2";
$config->{debug_section2}->{debug_attr3} = "dummy string 3";
write_ini( $ini_filename, $config );
printf( "new value (%s) written\n", $new_value );
# *************
# INIファイルに書き込む (Config::Tiny 互換)
# param[0]: string ファイル名
# return: hash 全ての値を格納したハッシュ{セクション名}{値名}
sub read_ini {
my ($filename) = @_;
open my $fh, '<', $filename or return undef;
my %config;
my $section = '';
while ( my $line = <$fh> ) {
chomp $line;
$line =~ s/^\s+|\s+$//g; # 前後の空白除去
next if $line eq '' or $line =~ /^;/; # 空行・コメント除外
if ( $line =~ /^\[(.+?)\]$/ ) {
$section = $1;
$config{$section} ||= {};
}
elsif ( $line =~ /^([^=]+?)\s*=\s*(.*)$/ ) {
my ( $key, $value ) = ( $1, $2 );
$config{$section}{$key} = $value;
}
}
close $fh;
return \%config;
}
# *************
# INIファイルに書き込む (Config::Tiny 互換)
# param[0]: string ファイル名
# param[1]: hash 全ての値を格納したハッシュ{セクション名}{値名}
sub write_ini {
my ( $filename, $config_ref ) = @_;
open my $fh, '>', $filename or return 0;
foreach my $section ( sort keys %$config_ref ) {
print $fh "[$section]\n";
foreach my $key ( sort keys %{ $config_ref->{$section} } ) {
my $value = $config_ref->{$section}{$key};
print $fh "$key=$value\n";
}
print $fh "\n";
}
close $fh;
return 1;
}
Stat::lsMode::format_modeを代替する関数
#!/usr/bin/perl
use File::Basename;
my $filepath;
if ( @ARGV == 1 ) {
$filepath = $ARGV[0];
}
else {
my $scriptname = basename( $0, '' );
print("usage : $scriptname [target filepath]\n");
exit(1);
}
print("target path = $filepath \n");
my @stats = stat($filepath);
my $attr_int = $stats[2];
# $attr_int = 0100755; # -rwxr-xr-x # debug
my $attr_formatted = format_mode($attr_int);
printf( "attr : %s (%X, %o)\n", $attr_formatted, $attr_int, $attr_int );
# *************
# stat の第3要素(モード情報)を人間が読める形式(例: -rw-r--r--)に変換する関数 (Stat::lsMode::format_mode と互換の関数)
sub format_mode {
my ($mode) = @_;
# ファイルタイプ
my %file_types = (
0xC000 => 's', # socket
0xA000 => 'l', # symbolic link
0x8000 => '-', # regular file
0x6000 => 'b', # block device
0x4000 => 'd', # directory
0x2000 => 'c', # character device
0x1000 => 'p', # FIFO
);
my $type_char = '?';
foreach my $type ( keys %file_types ) {
if ( ( $mode & 0xF000 ) == $type ) {
$type_char = $file_types{$type};
last;
}
}
# パーミッション
my @rwx = qw(r w x);
my $perm_str = '';
for my $i ( 0 .. 2 ) {
my $shift = 6 - $i * 3;
my $bits = ( $mode >> $shift ) & 0x7;
for my $j ( 0 .. 2 ) {
$perm_str .= ( $bits & ( 1 << ( 2 - $j ) ) ) ? $rwx[$j] : '-';
}
}
# setuid, setgid, sticky bit の処理
substr( $perm_str, 2, 1 ) =
( $mode & 0x800 ) ? ( ( $perm_str =~ /^..x/ ) ? 's' : 'S' ) : substr( $perm_str, 2, 1 );
substr( $perm_str, 5, 1 ) =
( $mode & 0x400 ) ? ( ( $perm_str =~ /^.....x/ ) ? 's' : 'S' ) : substr( $perm_str, 5, 1 );
substr( $perm_str, 8, 1 ) =
( $mode & 0x200 ) ? ( ( $perm_str =~ /^........x/ ) ? 't' : 'T' ) : substr( $perm_str, 8, 1 );
return $type_char . $perm_str;
}
Copilot AIがsetuid, setgid, sticky bitを検出するビット計算を間違っていたので、そこだけ修正したのが上のソースコード