Sep 3
#!/bin/bash

API=https://dnsapi.cn/Record.Ddns

IP_FILE=/tmp/dnspod_ip

function get_old_ip()
{
    ip=
    if [ -e "$IP_FILE" ]; then
        ip=`cat $IP_FILE`
    fi
    echo $ip
}

function save_ip()
{
    echo -n $1 > $IP_FILE
}

function get_new_ip()
{
    echo `nc ns1.dnspod.net 6666`
}

email=帐号邮箱
password=帐号密码  #dnspod就不能搞成个secret_key么!明文密码让人很不舒服啊。

domain_id=XXXXX #使用Domain.List API获取
record_id=YYYYY #使用Record.List API获取
sub_domain="zzz" #DDNS的二级域名

new_ip=`get_new_ip`
old_ip=`get_old_ip`

if [ "$new_ip" != "$old_ip" ];
then
    curl $API -d "format=json&login_email=$email&login_password=$password&domain_id=$domain_id&record_id=$record_id&sub_domain=$sub_domain&record_line=默认"
    save_ip $new_ip
fi

然后加入crontab,每隔15分钟跑一次进行更新

*/15 * * * * ~/bin/dnspod.sh
Aug 24
:set formatoptions+=m    "允许对multi_byte字符换行(否则默认只能空格或者英文标点,详见set breakat=
:set textwidth=80    "换行的长度
ggVG  "选中全文
gq "应用到选中文本

p.s. 最后两句也可以缩写成 gggpG (少一个字符)..
Aug 22

windows下的批量重命名 不指定

felix021 @ 2012-8-22 22:23 [IT » 其他] 评论(2) , 引用(0) , 阅读(7092) | Via 本站原创
可能很多同学不知道Windows是自带这个功能的。至少从WinXP开始就有。操作要领:

0. 批量重命名的文件应该在一个文件夹内。

1. 选择所有需要被重命名的文件。全选(CTRL+A),或拖动/用shift选择选择一个区间,或按住CTRL一个一个点,都可以。

2. 使用鼠标右键选择你希望被重命名的第一个文件,输入新的文件名。注意,新的文件名如果不带编号(如a.txt),那么会按顺序改成 a.txt, a (1).txt ... a (5).txt,如果带编号(如 b (3).txt,注意编号只能用圆括号包起来),则编号会按顺序增加:b (3).txt, b (4).txt ... b (11).txt。

3. 回车,OVER。

功能很贴心,但是半残,有时候不满足要求,在没有安装额外的语言的情况下,可以用类似如下的vbs来应急:
Set a= WScript.createObject("WScript.Shell")
WScript.sleep 1000
For i = 1 to 23
  j = i
  If i < 10 Then
    j = "0" & i
  End If
  a.sendkeys "{DOWN}{F2}"    '向下,F2(重命名)
  a.sendkeys "TBBTs02e" & j  '文件名, win7下就不用再  & ".rmvb" 了,但是xp需要
  a.sendkeys "~"            '回车
  WScript.sleep 200
Next
Aug 17
对ssh用得比较多的同学应该知道通过建立信任关系来免除输入密码的麻烦:

在A机器上执行:

$ ssh-keygen -t rsa (各种回车)
$ ssh-copy-id -i ~/.ssh/id_rsa.pub USER@B_ip

然后在A机器上 ssh USER@B_ip 就可以免密码使用USER用户登录B机器了。

实际上第二步操作是将 A 机器该用户的公钥(id_rsa.pub)追加到B机器的 ~/.ssh/authorized_keys 文件末尾中去。

当A机器访问B时,如果B机器的sshd能够在/home/USER/.ssh/authorized_keys中找到对应的公钥,就认为A机器具有B机器的USER用户访问权限,于是就直接让A机器以USER身份登录。

但是上周在线上某台机器进行操作时却发现这一机制失效了。通过该机制,A=>B可登录,但是B=>A失败,甚至A=>A也失败(B=>B却成功)。虽然问题很奇怪,但说明问题出在A机器上。

首先是 diff 了A、B两台机器的 /etc/ssh ,发现完全相同,所以不是配置的问题。

然后查看 ssh -vv localhost
引用
....
debug1: Authentications that can continue: publickey,password
debug1: Next authentication method: publickey
debug1: Offering RSA public key: /home/felix021/.ssh/id_rsa
debug2: we sent a publickey packet, wait for reply
debug1: Authentications that can continue: publickey,password
debug1: Trying private key: /home/felix021/.ssh/id_dsa
debug1: Trying private key: /home/felix021/.ssh/id_ecdsa
debug2: we did not send a packet, disable method
debug1: Next authentication method: password

而在B机器上,we sent a public key packet, wait for reply 之后则是紧跟着"debug1: Server accepts key: pkalg ssh-rsa blen 279"。由此可以看出,是A机器的sshd不认可publickey。

至于为什么不认可,在google上查了许多,毫无头绪,直到使用类似“ssh publickey ignore debug diagnose”这样的关键词,发现这个页面,其中的第二条和第六条给出了解答:
引用
2. Debugging on the remote host by running sshd in debug mode: Run ‘/usr/sbin/sshd -d -p 2222′ on the remote host and connect to it. ’2222′ here is the port number of the sshd process you started on the remote host.

6. Check the permissions on your home directory, .ssh directory, and the authorized_keys file: If your ssh server is running with ‘StrictModes on’, it will refuse to use your public keys in the ~/.ssh/authorized_keys file. Your home directory should be writable only by you, ~/.ssh should be 700, and authorized_keys should be 600.


通过执行 /usr/sbin/sshd -d -p 2222 (在2222端口启动一个带debug输出的sshd) ,然后 ssh -vv localhost -p 2222 ,可以看到sshd输出了一行
引用
Authentication refused: bad ownership or modes for directory /home/felix021

正好与那第六条相对应,再检查一下 /home/felix021 ,其权限是其他组可写。

最终解决方案:将用户home目录的权限改为0755,登录成功。
Aug 8
这是第一个支持IBM PS/2和1.44MB 3.5寸软盘的DOS版本,可能是在家用PC上比较容易用虚拟机模拟的最古老的微软系操作系统。隐约记得这里头还有个BASICA解释器,比QBASIC还要老很多的那种,类似于文曲星上面的那个版本。
下载文件 (已下载 1742 次)

以前(大一之前)很喜欢玩这些东西,收集了不少东西,包括3.31, 6.22, win98/me/xp的DOS启动盘, windows 1.0 ~ 3.2(主要来自曾经的“bear5的软件地摊”),还有很多dos下的工具,tw ucdos 之类,甚至还有个VB DOS版。折腾bat、config.sys这些东西,玩得乐此不疲。如今突然想起,却发现它们静静地躺在硬盘的那个角落已经好多年了。

谨以此纪念那些二逼的时光。
Jul 26
如果到Google去搜索,"How to find out which process is listening upon a port"这是第一篇文章。

事实上大部分文章都是告诉你,要么 lsof -i :80 要么 netstat -antulp | grep :80 就能找到httpd。

可是如果就这样的话,这篇BLOG就变成微博了。

事实上我的目的是希望通过编程找出这个PID,而不是调用某个命令。

第一个尝试是去看lsof的源码。找源码容易,apt-get source lsof 就行。但是源码跟大部分linux软件包一样,看起来相当晦涩。

第二个尝试是Google,但是能找到的都是命令版的。

第三个尝试是stackoverflow,没有直接搜到问题,于是准备自己提问,但是在“Questions that may already have your answer”里头找到了一篇“How to get the pid of a process that is listening on a certain port programmatically?” ( http://stackoverflow.com/questions/10996242 )。

Answer给出的步骤非常清晰:与netstat的实现一样,先读取 /proc/net/tcp 这个文件,第二个字段(local_address)冒号后面的是端口号(十六进制),第四个字段st为0A表示TCP_LISTEN,第10个字段是十进制的inode编号;而通过遍历 /proc/PIDs/fd 下面的链接,找到链接到形如 socket:[端口号] 的fd进行对比,就能知道哪些进程与该端口有一腿。

我在机器上监听的是8888端口,换成hex是22B8,但是在 /proc/net/tcp 中却找不到。幸而那篇文章的第二个Answer给了个提示,于是 strace /usr/sbin/lsof -i :8888 ,发现它还打开了 /proc/net/tcp6 (也就是对应IPv6的那个文件了)。过去一查,果然有,再顺着inode,对照lsof的结果一看,的确符合。

于是再去grep一把 lsof 的源码目录,发现在 00FAQ 文件中的 10.2.2 一节就说明了 lsof 的实现机制:
引用
Lsof identifies protocols by matching the node number associated
    with the /proc//fd entry to the node numbers found in
    selected files of the /proc/net sub-directory.  Currently
    /proc-based lsof examines these protocol files:
        /proc/net/ax25      (untested)
        /proc/net/ipx      (needs kernel patch)
        /proc/net/raw        /proc/net/raw6
        /proc/net/tcp        /proc/net/tcp6
        /proc/net/udp        /proc/net/udp6
        /proc/net/unix

看来在 Linux 下的实现方式确实只有这一种。顺便提一下,Stackoverflow上面的另一篇 http://stackoverflow.com/questions/4041003/c-what-process-is-listening-on-a-certain-port-in-windows 提到了,在Windows下可以用GetExtendedTcpTable/GetExtendedUdpTable来实现。

最后附上PHP实现的源码(这个代码用C/C++写确实蛋疼)
<?php

$filelist = array("/proc/net/tcp6", "/proc/net/tcp"); //udp/unix的就先不管了

$port2inode = array();

foreach ($filelist as $file)
{
    $lines = file($file);
    array_shift($lines);
    foreach ($lines as $line)
    {
        $values = split(" +", $line);
        list($addr, $port) = explode(":", $values[2]);
        $port = hexdec($port);
        $port2inode[$port] = $values[10];
    }
}

if ($argc < 2)
    die("usage: php {$argv[0]} PORT\n");
$findport = $argv[1];

if(!isset($port2inode[$findport]))
    die("$findport not listened\n");

$procdir = scandir("/proc");
natcasesort($procdir);

foreach ($procdir as $pid)
{
    $path = "/proc/$pid/fd";
    if (!is_readable($path) || !is_numeric($pid) || !is_dir($path)) continue;
    $dir = scandir($path);
    foreach ($dir as $file)
    {
        $link = "$path/$file";
        if (!is_link($link)) continue;
        $real = readlink($link);
        if (substr($real, 0, 7) == "socket:")
        {
            $port = substr($real, 8, -1);
            if ($port == $port2inode[$findport])
            {
                echo $pid, ": ", readlink("/proc/$pid/exe") . "\n";
                continue;
            }
        }
    }
}
?>
Jun 30

二进制偶矩阵 不指定

felix021 @ 2012-6-30 22:34 [IT » 程序设计] 评论(2) , 引用(0) , 阅读(7503) | Via 本站原创
这是2012年百度实习招聘非技术类的某道笔试题。

给一个5×5的矩阵,每个元素只能是0或者1。

请问,有多少种不同的矩阵,能够满足每一行、每一列都有偶数个1?

==== 分割线 ====

乍看这个题目,觉得是数学题。画了个5*5的矩阵,试图填几个数字进去看看是否可以推出一些结论。果断失败。

然后想了下,这题如果枚举的话,也就是2的25次方,大约3200万这个规模,不是很夸张。于是决定暴力搞一下。

最简单的做法就是
for (i = count = 0; i < 2<<25 - 1; i++) check_even(i) && count++;
这个check_even(i)里头把 i 当成一个25bit的二进制数字,并转换为对应的5*5矩阵,判断其每一行和每一列是否满足要求。(p.s. whusnoopy的做法是直接使用位运算,更简单,不过思路就断了。。)

一个不难想到的优化是,在for之前先把每一行给枚举了,这样就不需要在check_even里面每次进行转换,只需要从 i 中取出对应的bits,就可以直接找到每一行。

更进一步,由于题目要求每一行都是偶数个1,所以可以进行剪枝——在枚举的时候只需要保留有偶数个1的情况就行了,枚举出5行,然后判断每个列。很容易计算,每行5个bit,偶数个1的情况是2^4=16种。于是需要枚举的矩阵数量降至16^5。

再进一步剪枝——题目要求所以列是偶数,那么在已经确定前4行的情况下,第5行是可以直接推出来的,需要枚举的矩阵数量降至16^4。但还需要做的事情是,判断第5行是否有偶数个1。

到了这一步,豁然开朗——因为很容易证明,第5行必然是偶数个1:
  1) 每一列都是偶数个1(ABCDE都是偶数),所以矩阵中必然有偶数个1(F=A+B+C+D+E为偶数)
  2) 前4行都是偶数个1(HIJK都是偶数),所以第5行必然是偶数个1(L=F-H-I-J-K为偶数)
  (p.s. 这个证明是WHUMSTC群里某同学给出的,非常清晰,所以我就不给我自己那个很挫的证明过程了)

于是开头的直觉获胜,问题的答案就是:16^4,也就是(2^4)^4。

==== 分割线 ====

扩展:

1. 如果矩阵的大小是 N×N ,甚至是 M×N 呢?

    根据上述结论,很容易推知,对于M*N的矩阵,结果是2^((M-1) * (N-1))。

2. 如果要求满足每一行、每一列都有奇数个1呢?(whusnoopy提出)

    这个结论就不那么直接了,对M*N有一定的限制。
May 20
从哪儿说起呢?我想了想,从 gets 说起可能最好。

初学C语言的时候,如果要输入一行字符串,该怎么办?看书,或者找老师,或者找学长,通常得到的答案是gets。用法很简单,似乎也很好用,但是很不幸,这个函数很危险。因为 gets 对输入不进行任何的限制。如果对应的字符数组只有100个字符,而面对的输入是1万个字符,那么几乎毫无疑问,这个程序是要崩溃的,除非运气特别好,或者……

或者给出的输入是经过精心设计的,例如一段shell code,及其对应的跳转地址。对于常见的计算机体系来说,函数调用时,返回地址是在栈上的,通过精心设计输入,使得溢出数据中的跳转地址好正好覆盖了该返回地址,于是函数在返回时不是如预期般回到调用者处,而是跳转到攻击者给出的shell code处,使得攻击者获得了额外的权限。

这就是典型的溢出攻击。

为了防止这种情况的出现,在C库函数中,许多对字符串操作的函数都有其"n兄弟"版本,例如strncmp,strncat,snprintf……兄弟版本的基本行为不变,但是通常在参数中需要多给出一个整数n,用于限制操作的最大字符数量(本句不够严谨,详情参见各函数的说明)。

这是技术上的解决方案。只是,代码都是人写出来的,总会有对溢出缺乏概念的人,写出令人蛋疼的代码。于是一些公司,例如(听说)腾讯,建立了一套规则,对提交的代码进行扫描,若发现使用了非“n兄弟”版本,就会给对应的码农一定的惩罚措施,从而在管理上降低此类问题出现的可能性。

加强管理当然是好事,但是也给某些有强迫症的码农带来了不便:因为strlen没有n兄弟版本,坑爹啊!事实上,更坑爹的是strcpy,在c语言标准里,它不但没有n兄弟版本,甚至还有一个“冒充”的"n兄弟"版本——也就是 strncpy 。

strncpy 到底做了什么事情呢?它基本上等同于这样几行代码:
char* strncpy(char *dest, const char *src, size_t n){
    size_t i;
    for (i = 0 ; i < n && src[i] != '\0' ; i++)
        dest[i] = src[i];
    for ( ; i < n ; i++)
        dest[i] = '\0';
    return dest;
}

比较诡异的两件事情是:

1. 如果src的前n个字符里面没有'\0',那么它不会在末尾补上这个结束符

2. 如果拷贝的数据不满n个字符,那么它会用 '\0' 在末尾填充

以 strcpy 的行为来理解它,只会感到很蛋疼:第一点很可能会造成此后代码的数组越界访问,而第二点则是对cpu资源的浪费。

事实上,完全是因为历史的原因,造成了这样的误会。在第七版的UNIX文件系统中,每个inode结构体中包含的每个entry(对应文件或下级目录)只有16个字节,其中前两个用于标识inode,剩下的14个用于保存文件名。由于文件名最长只能有14个字符,所以在设计上,末尾不足的字符用'\0'来填充;如果达到14个字符,则不需要结束标志。

众所皆知,c是为unix而生,所以这就是strncpy的原始目的:定长字符串 的拷贝。对应的代码,很自然地,可以这样写:
strncpy(inode->d_name, filename, 14);

那么如果确实需要一个strcpy的n兄弟版本该怎么办呢?最简单的办法是用snprintf:
snprintf(dest, n, "%s", src);//注意,不能直接用src来替换"%s"

p.s. 其实还有个 strlcpy ,只可惜它是OpenBSD 2.4引入的,并非C标准中的函数,适用范围较窄。

参考资料:
http://www.lysator.liu.se/c/rat/d11.html
http://stackoverflow.com/questions/1453876/why-does-strncpy-not-null-terminate
http://stackoverflow.com/questions/2884874/when-to-use-strncpy-or-memmove
http://blog.liw.fi/posts/strncpy/
http://pubs.opengroup.org/onlinepubs/9699919799/functions/stpncpy.html
分页: 17/99 第一页 上页 12 13 14 15 16 17 18 19 20 21 下页 最后页 [ 显示模式: 摘要 | 列表 ]