vsftpd配置(xinetd模式)
vsftpd相关配置文件
- 配置文件路径:/etc/vsftpd
vsftpd.conf
anonymous_enable=NO local_enable=YES write_enable=YES local_umask=022 anon_upload_enable=NO anon_mkdir_write_enable=NO dirmessage_enable=YES xferlog_enable=YES connect_from_port_20=YES xferlog_file=/var/log/xferlog xferlog_std_format=YES idle_session_timeout=600 data_connection_timeout=120 # 此项设为NO,用于支持xinetd方式运行ftp服务。 listen=NO pam_service_name=vsftpd # 以下两项指定只允许userlist中的用户访问。 userlist_enable=YES userlist_deny=NO userlist_file=/etc/vsftpd/user_list tcp_wrappers=YES max_clients=5 max_per_ip=2
user_list
user1 user2
xinetd 相关配置文件
- 配置文件路径
- /etc/xinetd.conf
- /etc/xinetd.d
/etc/xinetd.d/vsftpd
service ftp
{
disable = no
id = vsftpd
wait = no
socket_type = stream
user = root
server = /usr/sbin/vsftpd
server_args = /etc/vsftpd/vsftpd.conf
port = 21
log_on_success += DURATION USERID
log_on_failure += USERID
nice = 10
umask = 022
}
重启xinetd服务。
service xinetd restart
Redis的复制
- 关于Redis复制的几个重要的方面
- 一个master可以有多个slave
- slave可以访问其他slave连接。除了多个slave连接同一个master的情况外,
slave可以连接其他slave,形成图状结构。 - master端的复制是非阻塞的,这意味着当一个或多个slave第一次同步时,master仍然可以对外提供查询服务。
- 在redis.conf文件里做了相应配置后,slave端的复制也可以是非阻塞的,
- 当slave执行首次同步时,它仍然可以使用旧的数据集提供对外查询服务。
- 如果和master的连接断掉,可以配置slave向客户端发送一个错误。
- 在某一时刻,当必须删除旧文件,新文件加载到slave时,slave会阻塞进来的连接。
- 复制可以用于提高服务的可伸缩性,或者是仅仅用于数据冗余。
- 可伸缩性方面,比如可以用多个slave来进行只读的查询,比如重型SORT可以交给slave来做。
- 可以用复制来让master端避免持久化过程:配置master端的redis.conf来避免saving(注释掉所有save相关配置行)
然后连接一个配置成经常保存的slave端。 - Redis的复制是如何工作的
- 如果配置好了一个slave,在连接后,它会发送SYNC命令。且它不关心自己是否是初次连接还是重新连接。
- 然后master启动后台保存,并且会将所有新的修改数据集的命令收集起来。当后台保存完成,master将
数据库文件传给slave,slave将文件保存到磁盘,然后加载到内存中去。从此之后,master将收集积累到的所有
新命令发给slave。 - 当master和slave之间的连接因为某些原因断开,slave会自动重连。如果master同时收到多个slave同步请求,
它只做一次后台保存。 - 连接断开后,master和slave进行重连时,会执行全面重新同步。
- 配置
- slave端配置文件:slaveof 192.168.1.1 6379
- slave端也可以调用SLAVEOF命令,master端会和slave开始同步。
- 只读slave
- TBC (Redis 2.6 特性,现有稳定版本2.4)
- slave认证
- 如果master配置了requirepass,则需要给slave的所有同步操作都需要设置密码。
- 命令行:config set masterauth <password>
- 或配置文件:masterauth <password>
OSX: 添加菜單欄圖標顯示
添加菜單欄圖標顯示:激活 Finder,按下 shift+蘋果鍵+G,而後在“前往文件夾”的窗口輸入“/System/Library/CoreServices/Menu Extras/”並確認“前往”,而後連按(雙擊)該目錄下的“Eject.menu”或將它直接拖至菜單欄中都可(Menu Extras 目錄內很多相關硬件/功能項的菜單欄圖標顯示)。
移除/取消圖標顯示:按住蘋果鍵直接用鼠標將菜單欄上的圖標拖拽下來,釋放鼠標後即可。
移動圖標顯示位置:按住蘋果鍵直接用鼠標在菜單欄範圍之內拖放圖標
How Android Draws Views (Android官方文档中文版)
Android如何绘制View
当得到焦点后,Activity会被系统请求绘制其自身的的layout。Android负责处理绘制的过程,但Activity必须提供其layout树形结构中的根节点。
绘制过程从布局的根节点开始。系统要求根节点测量(measure)和绘制(draw)layout树。绘制处理的过程是遍历整棵树,并渲染每一个与无效区域(invalid region)相交的View。每个View group依次请求它的每一个子节点进行绘制(使用draw()方法),每一个View负责绘制它自己。整棵树按顺序进行遍历,因此父节点将在其子节点之前先被绘制(这意味着先绘制的更处于后方)。兄弟节点按他们在树中出现的顺序依次绘制。
layout的绘制有2个过程:测量过程和布局过程。测量过程在measure(int,int)中实现,是一个自顶向下的对View树的遍历过程。在这个递归过程期间,每个View会将它的尺寸规格沿着树向下传递。
在测量过程的最后,每个View保存它们的尺寸。第二个过程发生在layout(int,int,int,int),也是自顶向下的过程。在此过程期间,每个父节点根据在测量过程中的计算结果,负责它的所有子节点的安放和布置。
| 不在无效区域中的View不会被framework绘制,同时framework会为你管理处于后台的View的绘制工作。可以通过调用invalidate()强制一个View进行绘制。 |
当View的 measure() 方法返回,它的 getMeasureWidth() 和 getMeasureHeight() 方法必须能返回有意义的值,根据这个规定,所有View的孩子也必须一致。View的测量后宽度和高度值,必须遵守其父节点规定的约束值。这保证了在测量过程的最后,所有的父节点能够接受所有他们的子节点的尺寸。父View可能会多次调用它的孩子的measure()方法。举个例子,在没定下来尺寸的时候,父节点可能会先测一次每个孩子来找出他们想要多大的尺寸,如果所有孩子的未受限之前的尺寸总和太大或者太小,会使用实际的数字对他们再次调用measure()。(即,如果孩子不同意它们之间所分配获得的空间大小,那么父节点将进行调停,并在第二个过程中设定规矩)。
测量过程使用2个类来负责尺寸的传递和通信。View 使用 View.MeasureSpec 类来告诉父节点如何测量和安放它们自己。基础的LayoutParams类描述View想要多大的宽度和高度。对于每一个尺寸规格,它可以指定如下值:
- 一个精确的数值。
- FILL_PARENT,意味着View想要尽量和它的父节点一样大。(减去填充值)
- WRAP_CONTENT,View想要适应其内容的大小。(加上padding值)
| 若要初始化或请求一个layout,调用 requestLayout()方法。典型地,当View确认它不再适合其当前边界时,它会调用自身的此方法。 |
不同的ViewGroup子类有对应的LayoutParams子类。例如,RelativeLayout有它自己对应的LayoutParams子类,包含了可以设置子View水平居中和垂直居中的能力。
MeasureSpecs 用于自树的父节点到子节点向下专递需求。MeasureSpec 可以为如下三种模式之一:
- UNSPECIFIED:由父节点使用,决定子View所需的尺寸。例如,LinearLayout可能会调用子节点的measure() 并设置height为UNSPECIFIED,width为EXACTLY 240,来找出在给予240像素宽度的情况下,子View的高度。
- EXACTLY:由父节点使用,为子节点强制指定一个实际的尺寸。子节点必须使用这个大小,并需要保证它的所有孩子都适应这个尺寸。
- AT_MOST:由父节点使用,指定子节点的最大尺寸。子节点必须保证它及它的所有孩子都不得超过此尺寸。
Fragments (Android官方文档中文版)
例如:当activity被暂停,那么在其中的所有fragment也被暂停;当activity被销毁,所有隶属于它的fragment也被销毁。然而,当一个activity正在运行时(处于resumed状态),我们可以独立地操作每一个fragment,比如添加或删除它们。当处理这样一种fragment事务时,可以将它添加到activity所管理的back
stack —— 每一个activity中的back stack实体都是一个发生过的fragment事务的记录。back
stack允许用户通过按下 BACK 按键从一个fragment事务后退(往后导航)。
hierarchy中的ViewGroup中,并且定义有它自己的view布局。通过在activity的布局文件中声明fragment来插入一个fragment到你的activity布局中,或者可以写代码将它添加到一个已存在的ViewGroup。然而,fragment并不一定必须是activity布局的一部分;如果愿意的话,也可以将一个fragment隐藏在activity的后台工作。
stack后,fragment如何维护它们的状态。在activity中,与activity和其他fragment共享事件。构建到activity的action
bar。以及更多内容。
hierarchy的复杂变化。通过将activity的布局分散到fragment中,可以在运行时修改activity的外观,并在由activity管理的back
stack中保存那些变化。
——
2个fragment并排显示在相同的一个activity中,并且每一个fragment拥有它自己的一套生命周期回调方法,并且处理它们自己的用户输入事件。因此,用这种方式来取代使用一个activity来选择一篇文章,而另一个activity来阅读文章的模式,
用户可以在相同的activity中选择一篇文章并且阅读, 如图所示:
当运行在一个特别大的屏幕时(例如平板电脑),app可以在Activity
A中嵌入2个fragment。然而,在一个正常尺寸的屏幕(例如手机)上,没有足够的空间同时供2个fragment用,因此,Activity
A 会仅包含文章列表的fragment,而当用户选择一篇文章时,它会启动Activity
B,它包含阅读文章的fragment。因此,应用可以同时支持图1中的2种设计模式。
类的代码看起来很像Activity。它包含了与activity类似的回调方法,例如onCreate()、onStart()、onPause(),以及onStop()。事实上,如果你准备将一个现成的Android应用转换到使用fragment技术,可能只需简单的将代码从activity的回调函数分别移动到fragment的回调方法。
- onCreate()当创建fragment时,系统调用此方法。在实现代码中,应当初始化想要在fragment中保持的必要组件,当fragment被暂停或者停止后可以恢复。
- onCreateView()fragment第一次绘制它的用户界面的时候,系统会调用此方法。为了绘制fragment的UI,此方法必须返回一个View,这个view是你的fragment布局的根view。如果fragment不提供UI,可以返回null。
- onPause()用户将要离开fragment时,系统调用这个方法作为第一个指示(然而它不总是意味着fragment将被销毁。)
在当前用户会话结束之前,通常应当在这里提交任何应该持久化的变化(因为用户有可能不会返回)。
《处理Fragment生命周期》
中讨论。
- DialogFragment显示一个浮动的对话框。用这个类来创建一个对话框,是除了使用Activity类的对话框工具方法之外的一个好的选择,因为你可以将一个fragment对话框合并到activity管理的fragment back
stack中,允许用户返回到一个之前曾被摒弃的fragment. - ListFragment显示一个由一个adapter(例如
SimpleCursorAdapter)管理的项目的列表,类似于ListActivity。它提供一些方法来管理一个list view,例如onListItemClick()回调来处理点击事件。 - PreferenceFragment显示一个 Preference对象的层次结构的列表,类似于 PreferenceActivity。这在为你的应用创建一个“设置”activity时有用处。
layout资源文件中读取并生成。为了帮助你这么做,onCreateView() 提供了一个 LayoutInflater
对象。
加载了一个layout:
|
public static class ExampleFragment @Override public // Inflate the layout for return inflater.inflate(R.layout.example_fragment, } } |
参数是你的fragment
layout将要插入的父ViewGroup(来自activity的layout)。savedInstanceState 参数是一个
Bundle,如果fragment是被恢复的,它提供关于fragment的之前的实例的数据。
- 想要加载的layout的resource ID。
- 加载的layout的父ViewGroup。传入container是很重要的,目的是为了让系统接受所要加载的layout的根view的layout参数,由它将挂靠的父view指定。
- 布尔值指示在加载期间,展开的layout是否应当附着到ViewGroup (第二个参数)。(在这个例子中, 指定了false, 因为系统已经把展开的layout插入到container –
传入true会在最后的layout中创建一个多余的view group。)
hierarchy的一部分被嵌入。 有2种方法你可以添加一个fragment到activity layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
属性指定了在layout中实例化的Fragment类。
layout时,它实例化每一个在layout中指定的fragment,并调用它们的onCreateView()方法,来获取每一个fragment的layout,系统将从fragment返回的
View 直接插入到<fragment>元素所在的地方。
- 为 android:id 属性提供一个唯一ID。
- 为 android:tag 属性提供一个唯一字符串。
- 如果以上2个你都没有提供,系统使用容器view的ID。
layout。只需简单的指定一个需要放置fragment的ViewGroup。为了在你的activity中操作fragment事务(例如添加、移除、或替换一个fragment),必须使用来自
FragmentTransaction 的API。
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container,fragment);
fragmentTransaction.commit();
ID指定,第二个参数是需要添加的fragment。一旦用FragmentTransaction作了改变,为了使改变生效,必须调用commit()。
fragment (第二个参数为fragment提供一个唯一的字符串"tag",而不是一个view
ID)。这么做虽然添加了fragment,但因为它没有关联到一个activity
layout中的某个view,所以不会接收到onCreateView()调用。因此不必实现此方法。
也可以提供字符串tag给有UI的fragment ——
但是如果fragment没有UI,那么这个tag是仅有的标识它的途径。如果随后你想从activity获取这个fragment,需要使用
findFragmentByTag()。
- 使用findFragmentById() (用于在activity
layout中提供UI的fragment)或findFragmentByTag()
(适用于有或没有UI的fragment)获取activity中存在的fragment。 - 使用 popBackStack() (模拟用户按下BACK
命令),将fragment从后台堆栈中弹出。 - 使用addOnBackStackChangeListener()注册一个监听后台堆栈变化的listener。
API 处理。我们也可以保存每一个事务到一个activity管理的back
stack,允许用户经由fragment的变化往回导航(类似于通过activity往后导航)。
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
add()、remove()、以及replace()。然后要给activity应用事务,必须调用 commit()。
stack。这个back stack由activity管理,并允许用户通过按下 BACK
按键返回到前一个fragment状态。
替换了当前layout容器中的由R.id.fragment_container标识的fragment。通过调用addToBackStack(),replace事务被保存到back
stack,因此用户可以回退事务,并通过按下BACK按键带回前一个fragment。
- 必须最后调用 commit()。
- 如果添加多个fragment到同一个容器,那么添加的顺序决定了它们在view hierarchy中显示的顺序。
addToBackStack(),那么当事务提交后,那个fragment会被销毁,并且用户不能导航回到它。有鉴于此,当移除一个fragment时,如果调用了
addToBackStack(),那么fragment会被停止,如果用户导航回来,它将会被恢复。
对于每一个fragment事务,你可以应用一个事务动画,通过在提交事务之前调用setTransition()实现。
并不立即执行事务。恰恰相反,它将事务安排排期,一旦准备好,就在activity的UI线程上运行(主线程)。如果无论如何有必要,你可以从你的UI线程调用 executePendingTransactions()
来立即执行由commit()提交的事务。但这么做通常不必要,除非事务是其他线程中的job的一个从属。
你只能在activity保存它的状态(当用户离开activity)之前使用commit()提交事务。
commitAllowingStateLoss()。
getActivity() 访问Activity实例,并且容易地执行比如在activity
layout中查找一个view的任务。
View listView = getActivity().findViewById(R.id.list);
findFragmentById() 或 findFragmentByTag()。
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
A),另一个显示文章内容(fragment B) —— 然后 framgent A必须告诉activity何时一个list
item被选中,然后它可以告诉fragment B去显示文章。
public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}
onArticleSelected() 来通知fragment B,从fragment
A传来了事件。为了确保宿主activity实现这个接口,fragment A的 onAttach()
回调方法(当添加fragment到activity时由系统调用)通过将作为参数传入onAttach()的Activity做类型转换来实例化一个OnArticleSelectedListener实例。
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
...
}
异常。正常情形下,mListener成员会保持一个到activity的OnArticleSelectedListener实现的引用,因此fragment
A可以通过调用在OnArticleSelectedListener接口中定义的方法分享事件给activity。例如,如果fragment
A是一个 ListFragment的子类,每次用户点击一个列表项,系统调用
在fragment中的onListItemClick(),然后后者调用 onArticleSelected()
来分配事件给activity。
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Append the clicked item's row ID with the content provider Uri
Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
// Send the event and Uri to the host activity
mListener.onArticleSelected(noteUri);
}
...
}
参数是被点击的项的行ID,activity(或其他fragment)用来从应用的 ContentProvider
获取文章。
提供菜单项给activity的选项菜单(以此类推, Action Bar也一样)。为了使这个方法接收调用,无论如何,你必须在
onCreate() 期间调用 setHasOptionsMenu() 来指出fragment愿意添加item到选项菜单(否则,
fragment将接收不到对 onCreateOptionsMenu()的调用)。
onOptionsItemSelected() 的回调。也可以在你的fragment layout中通过调用
registerForContextMenu() 注册一个view来提供一个环境菜单。当用户打开环境菜单,fragment接收到一个对
onCreateContextMenu()
的调用。当用户选择一个项目,fragment接收到一个对onContextItemSelected() 的调用。
- Resumed在运行中的activity中fragment可见。
- Paused另一个activity处于前台并拥有焦点,但是这个fragment所在的activity仍然可见(前台activity局部透明或者没有覆盖整个屏幕)。
- Stopped要么是宿主activity已经被停止,要么是fragment从activity被移除但被添加到后台堆栈中。停止状态的fragment仍然活着(所有状态和成员信息被系统保持着)。然而,它对用户不再可见,并且如果activity被杀死,它也会被杀死。
onSaveInstanceState() 期间保存状态,并可以在 onCreate()、onCreateView() 或
onActivityCreated() 期间恢复它。
- onAttach()当fragment被绑定到activity时被调用(Activity会被传入)。
- onCreateView()创建和fragment关联的view hierarchy时被调用。
- onActivityCreated()当activity的onCreate()方法返回时被调用。
- onDestroyView()当和fragment关联的view hierarchy被移除时调用。
- onDetach()当fragment从activity解除关联时被调用。
Mac下设置中转机端口转发
- 说明: 有时候因为工作保密性,登录公司的服务器,需要经过中转机的端口转发.
- 步骤:
- 打开终端, 执行ssh命令.
- 命令格式:
ssh -L localhost:本地端口:目标机器IP地址:目标机器端口 中转机用户名@中转机地址
- 举例:
ssh -L localhost:port:192.168.xx.xx:port username@xx.xxx.xx.xx
ObjC 实现类变量方法
(转自网络)
从HTTPRiot 源码学习而来。
@implementation HRRestModel// 全局静态变量,类变量就是用这个存储static NSMutableDictionary *attributes;+ (void)initialize { if(!attributes) attributes = [[NSMutableDictionary alloc] init];}// 防止继承的类使用相同的变量,很漂亮的代码+ (NSMutableDictionary *)classAttributes { NSString *className = NSStringFromClass([self class]); NSMutableDictionary *newDict; NSMutableDictionary *dict = [attributes objectForKey:className]; if(dict) { return dict; } else { newDict = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:HRDataFormatJSON] forKey:@"format"]; [attributes setObject:newDict forKey:className]; } return newDict;}// 设置类变量+ (void)setAttributeValue:(id)attr forKey:(NSString *)key { [[self classAttributes] setObject:attr forKey:key];} |
如何架设一个Cydia™ 库(译文)
原始文章: http://www.saurik.com/id/7
第一步: 安装包的制作
- 在Debian APT/dpkg的世界里,任何可以安装的事物都被称之为一个"包(package)",
- 包的存在形式是 .deb 文件, 其中包含:
- 将要安装的文件
- 包含了关于包的元数据(名称,大小,以及其他杂项)的控制信息.
- 我们使用工具dpkg-deb来构建那样一个文件. (Fink on a Macintosh)
- 为了制作一个包,我们需要准备一个文件夹,这个文件夹中包含了我们需要安装在iphone上的文件,
并且文件夹的结构和安装到iphone上后的文件夹结构一样. - 另外,我们还需要在包的根目录下增加一个文件夹DEBIAN, 用来存放包含了我们的元数据的文件 "control"
- 举例如下( 我们想安装一个名叫MyProgram的程序,以及一个 LaunchDaemon):
+- MyProgram +- Applications | +- MyProgram.app | +- Info.plist | +- MyProgram | +- icon.png +- DEBIAN | +- control +- System +- Library +- LaunchDaemons +- com.saurik.MyProgram.plist - control文件的内容是一系列的键/值对,由冒号分割, 每个键/值对一行
- 如下是一个完整的control文件, 以及逐项字段的说明:
Package: com.saurik.myprogram Name: MyProgram Version: 1.0.4-1 Architecture: iphoneos-arm Description: an example of using APT Every day people use Cydia, but with the instructions embodied in this package, people can publish for it as well. Homepage: http://www.saurik.com/id/7 Depiction: http://www.saurik.com/id/7 Maintainer: Your Name <you@example.com> Author: Jay Freeman (saurik) <saurik@saurik.com> Sponsor: Microsoft <http://www.microsoft.com/> Section: Games
- Package: 包的唯一标识, 必须小写. 域名的倒写,(更像apple的info.plist文件中的bundleIdentifier字段)
- Name: 在Cydia的列表上显示的名称. 任意字符串. 本字段可以经常改动, 而Package字段在包的生命期内都是固定的.
- Version: 包的版本号包括2个部分: 包中软件的版本号,以及包本身的版本号. 这2个版本号之间用"-"分隔.
- Architecture: 包所支持的系统, 由于.deb包可以用于iphone和台式机. 所以此处, iPhoneOS 1.0.x/1.1.x 对应的正确值是 "darwin-arm".
如果要部署到 iPhoneOS 1.2/2.x ,你应当使用 "iphoneos-arm". - Description: 这个字段比起其他字段来有一些复杂, 它可能会使用多行.
- 第一行(冒号之后)应当包含一个简短的描述,用于显示在包列表的包名称下方.
当然也可以在第一行加任意长的描述文本,这将会显示在包的详细视图屏幕上. - 从技术上讲,本字段的格式比较复杂,但是大多数的复杂性当前已经被Cydia忽略了:
Cydia允许你在这个字段中放置 HTML. - 扩展描述的每一行, 必须由空格开头.
- 不建议使用 HTML, 更简易使用Depiction字段
- 第一行(冒号之后)应当包含一个简短的描述,用于显示在包列表的包名称下方.
- Homepage: 主页地址
- Depiction: 一个URL,会在一个iframe中加载, 代替Description: 和 HomePage:, 大多数用于"More Info"
- Maintainer: 创建包的人. 维护者. 格式: "their name <email@addre.ss>".
- Author: 软件的原作者. 此名字会在详细视图的包名下方出现. 此字段格式同"Maintainer".
- Sponsor: 对此包的赞助者, 格式同"Maintainer", 使用URI代替EMail.
- Section: 包类型, 在Cydia的install的tab下面, 包以"Section"来区分列出. 此处,请以下划线代替空格.
- 然后,dpkg-deb程序就可以根据文件夹MyProgram创建包了, 输出是一个.deb包,可以被安装到iPhone并测试.
根据dpkg-deb的版本不同, 可能会收到一系列关于"user-defined fields"的警告. 这是因为Cydia定制的扩展的缘故,可以安全地忽略. - 如果你使用Macintosh, 在执行这些步骤之前,需要额外做一些处理. 当在Apple系统上创建tar文件(Debian包内部结构的一部分)时,
一系列额外的 ._* 文件(包含了fork资源信息)也会被创建. 然后这些文件夹会随着包安装,并且可能会和其他包冲突. 为了关掉它, 必须导出如下的环境变量:-
export COPYFILE_DISABLE export COPY_EXTENDED_ATTRIBUTES_DISABLE
-
[root@desktop:~/cydia]# dpkg-deb -b MyProgram
warning, `MyProgram/DEBIAN/control' contains user-defined field `Name'
warning, `MyProgram/DEBIAN/control' contains user-defined field `Homepage'
warning, `MyProgram/DEBIAN/control' contains user-defined field `Author'
dpkg-deb: building package `com.saurik.myprogram' in `MyProgram.deb'.
dpkg-deb: ignoring 3 warnings about the control file(s)
[root@desktop:~/cydia]# ls -la MyProgram.deb
rw-rr- 1 root root 906 2008-07-01 07:48 MyProgram.deb
[root@desktop:~/cydia]#
-
第二步: 测试安装包
- 现在,我们构造了我们的安装包, 可以简单地把它复制到一个iphone来测试. 由于.deb文件是完全的自包含, 这是相对容易的过程.
在iphone上, "dpkg -i" 可以用来从文件直接安装. 一旦安装完, 它可以被Cydia管理(将把包显示成"Local/Unknown"), 或者通过apt-get来操作. -
iPhone:~ root# dpkg -i MyProgram.deb
Selecting previously deselected package com.saurik.myprogram.
(Reading database ... 17090 files and directories currently installed.)
Unpacking com.saurik.myprogram (from MyProgram.deb) ...
Setting up com.saurik.myprogram (1.0.4-1) ...
iPhone:~ root# ls -la /Applications/MyProgram.app
total 0
drwxr-xr-x 2 root wheel 170 Jul 1 00:54 ./
drwxrwxr-x 73 root admin 2550 Jul 1 00:54 ../
rw-rr- 1 root wheel 0 Jul 1 00:47 Info.plist
rw-rr- 1 root wheel 0 Jul 1 00:47 MyProgram
rw-rr- 1 root wheel 0 Jul 1 00:47 icon.png
iPhone:~ root# apt-get remove com.saurik.myprogram
Reading package lists... Done
Building dependency tree... Done
The following packages will be REMOVED:
com.saurik.myprogram
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
Need to get 0B of archives.
After unpacking 0B of additional disk space will be used.
Do you want to continue [WXCP:Y/n]? y
(Reading database ... 17095 files and directories currently installed.)
Removing com.saurik.myprogram ...
iPhone:~ root#
第三步: 创建一个Cydia库
- 过程的最后一步是创建一个库, 让其他用户不仅仅用来安装,而且还可以通过Cydia找到你的软件.
这个步骤需要在某个网站创建一个包含2个文件的文件夹: 你的 .deb 文件 和 一个名为Packages的索引文件.
索引文件通过一个Perl脚本dpkg-scanpackages创建, 它根据目录的名字扫描.deb文件.
这里我们传入 -m 参数来输出"all files, including duplicates" 以及将/dev/null作为"override"文件(将引起一些我们可以忽略的警告) -
[root@desktop:/web/apt/xmpl]# dpkg-scanpackages -m . /dev/null >Packages
-
- Packages in archive but missing from override file: **
com.saurik.myprogram
- Packages in archive but missing from override file: **
Wrote 1 entries to output Packages file.
[root@desktop:/web/apt/xmpl]# bzip2 Packages
[root@desktop:/web/apt/xmpl]# ls -la *
rw-rr- 1 root root 906 2008-07-01 07:48 MyProgram.deb
rw-rr- 1 root root 380 2008-07-01 08:00 Packages.bz2
[root@desktop:/web/apt/xmpl]# -
- 附件中提供了Perl脚本dpkg-scanpackages, 但是这个脚本目前不支持mac下的fink,并且不能在iPhone下运行.
第四步: Cydia库元数据 (可选)
- 另一个关于库的特性是, 可以附加相关人的信息, 库的名字, 等等.. 这些信息保存在一个名为Release的文件中,其格式后面的例子所述.
这个文件严格来讲是可选的. 如果你不使用它, Cydia会从你的URL合成信息. 来显示在你的源所需要显示的详细信息.Origin: Saurik's Example for Cydia Label: Cydia Example Suite: stable Version: 0.9 Codename: tangelo Architectures: iphoneos-arm Components: main Description: An Example Repository from HowTo Instructions
- Origin: 库名称
- Label: 在包列表界面, Cydia展示库和类型包的来源. 这个区域空间并不大, 所以, 这个字段应该包含一个库名称的简短版本. 可以用来当作一个标签.
- Suite: 固定填写为"stable".
- Version: 用途不明
- Codename: 在一个"自动"的库里,你可能存储多个用于不同目标系统的部署包. 这个字段描述了我们当前寻找的是哪个部署包.
不关心这点的,你可以填写任何值, 甚至这个字段是可选的. - Architectures: 指定使用APT的目标处理器架构. 必须指定所有在你的包文件中出现的目标架构. 1.1.x 使用"darwin-arm", 2.x 使用"iphoneos-arm"
- Components: 固定填写为"main".
- Description: 描述,在package source的屏幕上, 列出来的仓库的简短描述.
第五步: 包签名 (可选)
- 最后一步, 为你的库签名. 用以保证你的用户下载的软件确实是来自于你,而不是来自于另一方
- 为了做到这些, 我们需要做:
- 首先将我们的Packages文件的MD5 sums添加到Release文件.
- 然后使用GnuPG(PGP的GNU版本)来生成一个密钥并为文件签名, 创建一个新的Release.gpg文件.
- 对于MD5 sum, 我们需要增加一个额外的字段 "MD5Sum", 并在它的额外行(以空格开头)中,写明 MD5Sum, 文件大小(按字节), 以及我们的关键文件的名称.
注意, 我们必须列出文件的压缩和非压缩版本的以上相关信息. -
MD5Sum: 07a4ca0b91734e0489874dc44bd55222 464 Packages 43e92f4ec43f4c39d8f4268c7418f353 380 Packages.bz2
- 现在,我们需要从我们的Release文件创建Release.gpg文件. 这个文件将会先被下载,然后被用来验证Release文件的合法性.
-
[root@desktop:/web/apt/xmpl]# gpg -abs -o Release.gpg Release
You need a passphrase to unlock the secret key for
user: "Jay Freeman (saurik) <saurik@saurik.com>"
1024-bit DSA key, ID AA31C175, created 2008-01-01Enter passphrase:
[root@desktop:/web/apt/xmpl]# ls -la Release.gpg
-rw-r--r-- 1 root root 189 2008-08-05 11:52 Release.gpg
[root@desktop:/web/apt/xmpl]# - 为了让签名能够在客户端被验证, 客户端需要安装我们的公钥. 通过使用程序 apt-key 来完成, 这是一个gpg的前端程序,为我们完成其他的工作和填写相关的参数.
-
iPhone:~ root# apt-key add saurik.pub
OK
iPhone:~ root#
第六步: (客户端)添加库
- 添加我们创建的库: 我们只需添加一行到文件 /etc/apt/sources.list (如果不存在,我们自己创建).
内容为整个库的base URL以及其子路径(通常是 "./" ) -
deb http://apt.saurik.com/xmpl/ ./
- 进入Cydia 开始安装 (作者此处Cydia版本较老)
-
iPhone:~ root# apt-get update
Ign http://apt.saurik.com ./ Release.gpg
Ign http://apt.saurik.com ./ Release
Ign http://apt.saurik.com ./ Packages/DiffIndex
Ign http://apt.saurik.com ./ Packages
Get:1 http://apt.saurik.com ./ Packages WXCP:349B
Fetched 33.5kB in 3s (8482B/s)
Reading package lists... Done
iPhone:~ root# apt-get install com.saurik.myprogram
Reading package lists... Done
Building dependency tree... Done
The following NEW packages will be installed:
com.saurik.myprogram
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 904B of archives.
After unpacking 0B of additional disk space will be used.
WARNING: The following packages cannot be authenticated!
com.saurik.myprogram
Install these packages without verification [WXCP:y/N]? y
Get:1 http://apt.saurik.com ./ com.saurik.myprogram 1.0.4-1 WXCP:904B
Fetched 904B in 0s (2515B/s)
Selecting previously deselected package com.saurik.myprogram.
(Reading database ... 17090 files and directories currently installed.)
Unpacking com.saurik.myprogram (from .../com.saurik.myprogram_1.0.4-1_darwin-arm.deb) ...
Setting up com.saurik.myprogram (1.0.4-1) ...
iPhone:~ root#



近期评论