分类存档: Experience

Windows 7 & Arch Linux U盘安装二合一

距离上次发文也一年多了, 真是时光飞逝. 这一年工作接触了很多也做了很多, 不过其实学得越多越发现自己是小白, 也不好意思写文章误导大众了><, 分享一些小经验吧.

话说最近转战Arch Linux, 不过U盘已经放了个Windows 7, 就想弄个双启动. 双启动就得给U盘重新装个引导呗, 不过折腾了半天syslinux没搞定, 启动时卡在那黑屏, 也没啥报错信息. 还是上GRUB吧. 以下是在U盘上安装GRUB并配置双启动的方法.

U盘的目录结构

由于我的U盘之前做成了Windows 7的安装盘, 所以弄成了如下的文件结构. 直接引导Windows 7的ISO谁有兴趣的话也可以尝试下.

  • Windows 7: 直接解压镜像至U盘根目录.
  • Arch Linux: 直接把ISO放于U盘/boot/isos/下.

GRUB的安装

在Ubuntu下直接用grub-install即可, 其它系统可查阅相关文档.

grub-install --boot-directory=/mnt/usb/boot /dev/sdb


其中/dev/sdb是U盘设备, 假设U盘被挂载到/mnt/usb目录下.

GRUB的配置

以下是grub.cfg, 在U盘的/boot/grub/下, 注意不要和你硬盘的/boot搞混了!

set timeout=10
set default=0

menuentry "Arch" {
 set isofile="/boot/isos/archlinux-2013.09.01-dual.iso"
 loopback loop /$isofile
 linux (loop)/arch/boot/x86_64/vmlinuz archisolabel=ARCH_201309 img_dev=/dev/disk/by-label/GSP1RMCULXF img_loop=$isofile earlymodules=loop
 initrd (loop)/arch/boot/x86_64/archiso.img
}

menuentry "Windows 7" {
 insmod part_msdos
 insmod ntfs
 insmod ntldr
 ntldr /bootmgr
}

其中Arch的引导需要指定下镜像文件所在设备的路径img_dev, 我这边是根据U盘的Label(img_dev=/dev/disk/by-label/GSP1RMCULXF)找的, 不知道有没有更好的办法.

至此从U盘启动的时候就会出现启动菜单, 当然也可以类似地加入Ubuntu等, 不过要注意的是其它发行版和Arch的ISO目录结构可能不太一样, 需要查阅下相关文档看怎么配置引导.

完结撒花><!


2014.04.16 Update

贴一个Ubuntu的配置:

menuentry "Ubuntu 13.10 Desktop amd64" {
 set isofile="/boot/isos/ubuntu-13.10-desktop-amd64.iso"
 loopback loop /$isofile
 linux (loop)/casper/vmlinuz.efi boot=casper img_dev=/dev/disk/by-label/GSP1RMCULXF iso-scan/filename=$isofile noprompt noeject
 initrd (loop)/casper/initrd.lz
}

Cisco IPSec VPN For Debian

最近倒腾了下OpenVPN, 发现虽然用起来比SSH速度快点但拨上去实在是太慢. 看到推上的巨巨们都在上Cisco IPSec, 索性决定也自己上一个. 倒腾了下发现配置也不是很复杂, 网上也有巨巨们十分详细的配置步骤, 只是自己在配的过程中也发现有几个地方需要特别注意下, 在此流水帐下吧.

服务端


racoon

服务端配置主要是racoon, 配置文件参考了这篇文章这篇文章, 在此非常感谢. 另外这篇关于如何兼容黑莓的文章讲得也非常清楚, 再次一并推荐下.

既然是Debian安装当然是apt-get:

apt-get install racoon

然后是几个配置文件:

# /etc/racoon/racoon.conf

path pre_shared_key “/etc/racoon/psk.txt”;
path certificate “/etc/racoon/certs”;

listen {
    isakmp SERVER.IP.ADDRESS [500];
    isakmp_natt SERVER.IP.ADDRESS [4500];
}
 
remote anonymous {
    exchange_mode aggressive, main, base;
    mode_cfg on;
    proposal_check obey;
    nat_traversal on;
    generate_policy unique;
    ike_frag on;
    passive on;
    dpd_delay 30;
     
    proposal {
        lifetime time 28800 sec;
        encryption_algorithm 3des;
        hash_algorithm md5;
        authentication_method xauth_psk_server;
        dh_group 2;
    }
}
 
sainfo anonymous {
    encryption_algorithm aes, 3des, blowfish;
    authentication_algorithm hmac_sha1, hmac_md5;
    compression_algorithm deflate;
}
 
mode_cfg {
    auth_source system;
    dns4 8.8.8.8;
    banner “/etc/racoon/motd”;
    save_passwd on;
    network4 10.12.0.100;
    netmask4 255.255.255.0;
    pool_size 100;
    pfs_group 2;
}

# /etc/racoon/psk.txt

# Group Name Group Secret
GROUP.NAME GROUP.SECRET

设置好psk.txt的权限:

chmod 600 /etc/racoon/psk.txt

# /etc/racoon/motd

# Banner
Welcome to Cisco IPSec!

Account

用户验证用的是系统的用户系统, 简单的新建一个用户即可, 为了安全性shell可设为/bin/false.

useradd -MN -b /tmp -s /bin/false USER
passwd USER

iptables

添加以下规则以开放相应端口和NAT转发.

iptables -A INPUT -p udp -–dport 500 -j ACCEPT
iptables -A INPUT -p udp –dport 4500 -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.12.0.0/24 -o eth0 -j MASQUERADE
iptables -A FORWARD -s 10.12.0.0/24 -j ACCEPT

重启服务器后以上规则将失效, 持久化参见这篇文章.

ipv4 forward

# /etc/sysctl.conf

net.ipv4.ip_forward=1

修改后使用如下命令使之生效:

sysctl -p /etc/sysctl.conf

客户端


Linux

Linux可以使用vpnc作为客户端, 配置文件如下:

# /etc/vpnc/default.conf

IPSec gateway SERVER.DOMAIN/IP
IPSec ID GROUP.NAME
IPSec secret GROUP.SECRET
IKE Authmode psk
Xauth username USER.NAME
Xauth password USER.PASSWORD
NAT Traversal Mode cisco-udp

之后可以使用vpnc-connect/vpnc-disconnect连接和断开VPN.

A*算法的Javascript实现

最近突发奇想想去实现下寻路算法, 于是就去研究了下A*, 考虑到画图方便直接拿js+canvas写了. Javascript的数组实在是不能忍啊, 于是Hack一把Array对象><. 闲话不多说, 上代码:

DEMO

Dnsmasq设置简介

说是简介其实也不想详细介绍, 具体官网上都有, 一搜便知. 这里只是简单地罗列下常用配置, 以供参考.

#不读取/etc/resolv.conf文件
no-resolv

#不扫描/etc/resolv.conf和/etc/dnsmasq.conf文件的改动,如果有改动直接重启程序即可
no-poll

#设置dns服务器
server=8.8.8.8

#指定*.domain.name用8.8.8.8解析
server=/domain.name/8.8.8.8
注意不是server=/*.domain.name/8.8.8.8

#解析*.domain.name到127.0.0.1
address=/domain.name/127.0.0.1

#无效的DNS服务器, 用于反DNS劫持
bogus-nxdomain=127.0.0.1

下列一个典型的配置文件, 用于tomato, 要使其生效请开启截获DNS端口.

#覆盖掉原有DNS设置
no-resolv

#对baidu.com及其子域名使用中国电信的DNS
server=/baidu.com/218.2.135.1

#对其它的域名使用Google DNS
server=8.8.8.8

C#控制台输出实时重定向

.Net Framework中的Process类和ProcessStartInfo类可以方便地控制进程的启动, 还可以方便地将控制台程序的输出重定向到Stream中, 然后显示到TextBox中或者记录到文件中. 不过要实时显示每一行的输出还得用点技巧, 方法如下.

启动进程前的准备.

//设置待启动程序的路径和要传递的参数
ProcessStartInfo info = new ProcessStartInfo(path, args));

//将标准输出流重定向, 需同时把UseShellExecute设置为false
info.RedirectStandardOutput = true;

//不使用操作系统外壳程序启动
info.UseShellExecute = false;

//不创建窗体
info.CreateNoWindow = true;

为了做到实时捕获, 需在进程结束前一直搜索输出流, 如有新的一行便读取并引发一个事件以让主程序窗体能够响应并将这行添加至TextBox.

if (process.Start())
{
    while (!process.HasExited)
    {
        string line = process.StandardOutput.ReadLine();
        if (line != null)
            OutputLineGet(line);
    }
    process.WaitForExit();
}

为了不让窗体假死, 将调用上述循环放至另一个的单独线程中.

public void Start()
{
    new System.Threading.Thread(new System.Threading.ThreadStart(this.StartConsoleProgram)).Start();
}


然后便是窗体响应该事件, 注意到是跨线程调用GUI, 需用委托.

public MainForm()
{
  InitializeComponent();

  //注册事件
  this.OutputLineGet += new OnOutputLineGetDelegate(MainForm_OutputLineGet);
}

public delegate void OnOutputLineGetDelegate(string line);
public event OnOutputLineGetDelegate OutputLineGet;

private void MainForm_OutputLineGet(string line)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new OnOutputLineGetDelegate(MainForm_OutputLineGet), line);
    }
    else
    {
        txtLog.AppendText(line);

        //添加换行符
        txtLog.AppendText("\r\n");
    }
}

这样便能一行一行实时捕获输出了, 而且主程序也可以响应窗体消息不至于假死.

jQuery实现滑动效果

最近网上似乎开始流行滑动效果, 比如说M$主页上的这个. 虽然M$的这个是为了配合Metro的主题, 但是滑动展示却确实广泛应用于各大网站的主页上. 网上实现滑动效果的js插件一抓一大把, 但是既然我们用了jQuery做Ajax, 何不想想jQuery框架内就能解决的方案.

解决大体思路是: 在一个wrapper div里放N个等大的sub div, 并列放置, 多余部分不显示, 滑动时改变各个sub div的水平位置即可实现slide. 当然为了做成"滑动", 要用到jQuery的.animate()方法(当然别的框架也可以).

并列放置一般来说是把每个sub div的float设成left, 不过float其实并不是并列排版的最佳方案, 因为sub div从wrapper中"浮"了出来不再属于wrapper, 造成wrapper包不住sub. 需在某处应用clear, 典型的方法是新建个空div应用clear: both. 这样做未免有点Hack的味道, 空的div也不容易理解.

另一个方法是将每个sub div的position设成relative, 然后将后一个的top设成-300px(如每个sub div高度为300px). 这里的top是相对于前一个div而言的, 所以后面第二个的top应设为-600px. 关于relative的排版参见这里.

CSS3中对于元素并列放置有更好的解决方案: display: inline-block. 允许像div这种块级元素拥有inline的特性. 强烈建议对于块级元素的并列排放使用inline-block而不是float.

我用了第二种方法(虽然事实上第三种方法更好), 不过怎么排版其实并不重要, 排成一排就行了.

对于wrapper, 用overflow: hidden隐藏多余的sub div.

向左滑动时, 在js中对所有sub div调用.animate({"left", "-=300px"}).
向右滑动时, 在js中对所有sub div调用.animate({"left", "+=300px"}).

未防滑过头, 滑动之前需判断是否已到底.

其它方法可能调用.animate()时渐变的属性略有不同, 相应调整即可. 不过.animate()可调的属性限于几个可以连续变化的数值属性, 具体可参阅jQuery的文档.

简言之, 选择一个合适的方法并列排版sub div, 遮掉多余部分, 滑动时左右调整位置, 就这么简单.

附: Source Code

nginx泛二级域名配置

nginx有着非常强大的rewrite功能, 结合正则表达式可以定义非常灵活的泛二级域名.

假设/home/www中分别有company1/和company2/两个网站, 希望分别有各自的二级域名.

在nginx配置文件中使用如下语句即可分离出二级域名, 保存至变量$subdomain, 默认为company1.

set $subdomain company1;
if ( $http_host ~* "^(.*)\.archangelsdy\.com$") {
    set $subdomain $1;
}

之后用root语句定位至对应的主目录.

root /home/www/$subdomain;

同样, fastcgi的转发也相应修改为:

fastcgi_param  SCRIPT_FILENAME  /home/www/$subdomain$fastcgi_script_name;

如上配置后, 对于某个二级域名便可重定向置/home中对应的目录. 显然, 用这种方便配置后, 以后添加二级域名只需要配置好域名解析并在/home中建立相应目录即可, 不需要修改nginx的配置文件也不需要重启nginx, 可批量添加二级域名三级域名等, 缺点是对于每个二级域名下的网站配置都是相同的.

双层SSH隧道研究

学校的内网无法直接出校, 需拨某VPN才能访问外网, 对外统一为一个ip. 然而最近由于那啥升级导致学校的国际出口及其不稳, 无法通过此VPN挂SSH proxy. 手头有一系服务器(Server A)对外有独立ip, 然而没有root权限, 基本上只能ls/cp/mv/rm/scp. 突然想到既然可以ssh上此服务器当然可以当成proxy出校, 而且由于独立ip基本没有对外访问的请求应该不至于触发某计数器, 于是尝试在此ssh tunnel中再与vps(Server B)构造一层tunnel.

由于没有root权限也改不了iptables, Server A自然只能当成ssh proxy出校用了, 在本地8000端口监听:

ssh -qTfnN -D 8000 user@server-a.com

尝试让本地的ssh请求走8000端口的proxy, 然而全局代理无效, 只能另想办法.

Google了一圈发现proxychains, 支持HTTP/SOCKS4/SOCKS5, 支持ssh/telnet/wget/ftp/apt/vnc/nmap等命令的转发, 再合适不过了.

直接apt-get装完, 编辑/etc/proxychains.conf如下, 具体可参照该文件内的注释:

dynamic_chain
chain_len=1
proxy_dns 
tcp_read_time_out 15000
tcp_connect_time_out 10000

[ProxyList]
socks5 	127.0.0.1 8000
socks4  127.0.0.1 8000

配置完后就可以在通过代理使用ssh了:

sudo proxychains ssh -qTfnN -D 7070 user@server-b.com

此后配置浏览器通过7070端口使用代理, 即可正常访问.

C++判断输入结束的讨论

先看2段代码:


int a;
while(cin>>a)
{
cout<<a<<endl;
}


int a;
while(cin.good())
{
 cin>>a;
 cout<<a<<endl;
}

乍一看, 两者似乎都能判断出输入结束, 然而, 当输入出现错误时, 看下列例子:

input:
1 2 a
output 1:
1
2
output 2:
1
2
2

第二种方法却多了一个输出. 仔细观察, 发现两者从输入流读取和判断结束的顺序存在不同:

Method 1:
从输入流读取->判断流的状态->输出->…
Method 2:
判断流的状态->从输入流读取->输出->…

当循环进行到第三次时, Method 1先进行读取, 发现’a’不满足int, 于是返回0, 跳出循环, 故有2次输出. 而Method 2 先进行判断, 由于此时刚刚成功读完第二个数据, 第三个还未读, 流的状态cin.good()为true, 故进入第三个循环, 尝试读入一个数, 发现’a’不满足int, 于是a的值不会被覆盖, a的值仍为2, 故又输出一个2. 之后由于刚才读取失败置cin.good()为false, 跳出循环.
可见, 用cin.good()判断输入结束是可能存在问题的, 需作如下修改:

int a;
cin>>a;
while(cin.good())
{
cout<<a<<endl;
}

此时先进行读取再判断流的状态, 可以正常判断输入结束.

另外, 参考http://www.cplusplus.com的解释, 如下情况时cin.good()会被置false:

“The function returns true if none of the stream’s error flags (eofbit, failbit and badbit) are set.”

即eofbit, failbit, badbit中任何一个被触发即置cin.good()为false.
注意到cin.bad()并不是cin.good()的反面, 它只对应badbit.
关于这三个标志何时被触发有下表:

flag error
eofbit The end of the source of characters is reached during its operations.
failbit The input obtained could not be interpreted as an element of the appropriate type.

Notice that some eofbit cases will also set failbit.

badbit An error other than the above happened.

输入结束将导致eofbit和failbit, 输入类型不符将导致failbit, 其它将导致badbit.

吐槽: 这货害我在ZOJ上浪费了不下30次submit…

IIS上WordPress的固定链接问题

IIS上WordPress的固定链接不能做成这样的形式:
http://archangelsdy.com/2010-12/iis-wp-static-link/
上面的链接默认会404, 只能弄成如下形式:
http://archangelsdy.com/index.php/2010-12/iis-wp-static-link/
这样未免不太完美, 不过既然会404, 就可以在404页面上动点手脚, 一下是网上流传较广的404重定向修改页:


<?php
header("HTTP/1.1 200 OK");
$ori_qs = $_SERVER['QUERY_STRING'];
$pattern = '/[^;]+;[^:]+://[^/]+(/[^?]*)(?:?(.*))?/i';

preg_match($pattern, $ori_qs, $matches);
$_SERVER['PATH_INFO'] = $matches[1] . '?' . $matches[2];
$_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];
$query_args = explode('&', $matches[2]);
unset($_GET);
foreach ($query_args as $arg)
{
$the_arg = explode('=', $arg);
$_GET[$the_arg[0]] = $the_arg[1];
}

include('index.php');
?>

这个404.php挂了上去后, 链接是重定向正常了, 不过顶上华丽地顶着3个”offset error”, 貌似是数组越界, 看了下代码(其实完全不懂php…), 用到了$matches[]这个数组, 第一反应是添行if(count($matches)>=3)上去, 没想到真的成功了! 不知道有没有什么后遗症捏…

附改过的404.php, 有的主机可能要另外设一下404页面的位置.


<?php
header("HTTP/1.1 200 OK");
$ori_qs = $_SERVER['QUERY_STRING'];
$pattern = '/[^;]+;[^:]+://[^/]+(/[^?]*)(?:?(.*))?/i';

preg_match($pattern, $ori_qs, $matches);
if(count($matches)>=3)
{

$_SERVER['PATH_INFO'] = $matches[1] . '?' . $matches[2];
$_SERVER['REQUEST_URI'] = $_SERVER['PATH_INFO'];
$query_args = explode('&', $matches[2]);
unset($_GET);
foreach ($query_args as $arg)
{
$the_arg = explode('=', $arg);
$_GET[$the_arg[0]] = $the_arg[1];
}

}
include('index.php');
?>