Skip to content

Alpine Linux 包管理工具 apk 中的彩蛋

经常用Docker的朋友都不陌生Alpine Linux这个发行版。ARM64下的Alpine Linux v3.17镜像,体积只有惊人的7.46MBAlpine Linux提供了apk命令用于管理软件包。此apk(Alpine Package Keeper)非彼apk(Android Package),是Alpine Linux自带的包管理工具。

在终端中输入apk:

bash
$ apk
apk-tools 2.12.10, compiled for aarch64.

usage: apk [<OPTIONS>...] COMMAND [<ARGUMENTS>...]

Package installation and removal:
  add        Add packages to WORLD and commit changes
  del        Remove packages from WORLD and commit changes

System maintenance:
  fix        Fix, reinstall or upgrade packages without modifying WORLD
  update     Update repository indexes
  upgrade    Install upgrades available from repositories
  cache      Manage the local package cache

Querying package information:
  info       Give detailed information about packages or repositories
  list       List packages matching a pattern or other criteria
  dot        Render dependencies as graphviz graphs
  policy     Show repository policy for packages
  search     Search for packages by name or description

Repository maintenance:
  index      Create repository index file from packages
  fetch      Download packages from global repositories to a local directory
  manifest   Show checksums of package contents
  verify     Verify package integrity and signature

Miscellaneous:
  audit      Audit system for changes
  stats      Show statistics about repositories and installations
  version    Compare package versions or perform tests on version strings

This apk has coffee making abilities.
For more information: man 8 apk

倒数第二行有句话:This apk has coffee making abilities.(本 apk 有泡咖啡的能力。)

熟悉Debian的朋友可能知道,apt包管理工具也有这样一行字:This APT has Super Cow Powers.(本 APT 具有超级牛力)。输入apt moo即可触发这一彩蛋:

bash
$ apt moo
                 (__)
                 (oo)
           /------\/
          / |    ||
         *  /\---/\
            ~~   ~~
..."Have you mooed today?"...

但是在Alpine Linux中,apk coffee并不会为我们端上一杯咖啡:

bash
ERROR: 'coffee' is not an apk command. See 'apk --help'.

所以这个彩蛋的正确触发方式是什么呢?

查看apk工具的源码src/app_fetch.c,可以看见有一个神秘的函数:

c
static int cup(void)
{
	/* compressed/uncompressed size is 259/1213 */
	static unsigned char z[] = {
		0x78,0x9c,0x9d,0x94,0x3d,0x8e,0xc4,0x20,0x0c,0x85,0xfb,0x9c,
		0xc2,0x72,0x43,0x46,0x8a,0x4d,0x3f,0x67,0x89,0x64,0x77,0x2b,
		0x6d,0xbb,0x6d,0x0e,0x3f,0xc6,0x84,0x4d,0x08,0x84,0x55,0xd6,
		0xa2,0xe0,0xef,0x7b,0x36,0xe1,0x11,0x80,0x6e,0xcc,0x53,0x7f,
		0x3e,0xc5,0xeb,0xcf,0x1d,0x20,0x22,0xcc,0x3c,0x53,0x8e,0x17,
		0xd9,0x80,0x6d,0xee,0x0e,0x61,0x42,0x3c,0x8b,0xcf,0xc7,0x12,
		0x22,0x71,0x8b,0x31,0x05,0xd5,0xb0,0x11,0x4b,0xa7,0x32,0x2f,
		0x80,0x69,0x6b,0xb0,0x98,0x40,0xe2,0xcd,0xba,0x6a,0xba,0xe4,
		0x65,0xed,0x61,0x23,0x44,0xb5,0x95,0x06,0x8b,0xde,0x6c,0x61,
		0x70,0xde,0x0e,0xb6,0xed,0xc4,0x43,0x0c,0x56,0x6f,0x8f,0x31,
		0xd0,0x35,0xb5,0xc7,0x58,0x06,0xff,0x81,0x49,0x84,0xb8,0x0e,
		0xb1,0xd8,0xc1,0x66,0x31,0x0e,0x46,0x5c,0x43,0xc9,0xef,0xe5,
		0xdc,0x63,0xb1,0xdc,0x67,0x6d,0x31,0xb3,0xc9,0x69,0x74,0x87,
		0xc7,0xa3,0x1b,0x6a,0xb3,0xbd,0x2f,0x3b,0xd5,0x0c,0x57,0x3b,
		0xce,0x7c,0x5e,0xe5,0x48,0xd0,0x48,0x01,0x92,0x49,0x8b,0xf7,
		0xfc,0x58,0x67,0xb3,0xf7,0x14,0x20,0x5c,0x4c,0x9e,0xcc,0xeb,
		0x78,0x7e,0x64,0xa6,0xa1,0xf5,0xb2,0x70,0x38,0x09,0x7c,0x7f,
		0xfd,0xc0,0x8a,0x4e,0xc8,0x55,0xe8,0x12,0xe2,0x9f,0x1a,0xb1,
		0xb9,0x82,0x52,0x02,0x7a,0xe5,0xf9,0xd9,0x88,0x47,0x79,0x3b,
		0x46,0x61,0x27,0xf9,0x51,0xb1,0x17,0xb0,0x2c,0x0e,0xd5,0x39,
		0x2d,0x96,0x25,0x27,0xd6,0xd1,0x3f,0xa5,0x08,0xe1,0x9e,0x4e,
		0xa7,0xe9,0x03,0xb1,0x0a,0xb6,0x75
	};
	unsigned char buf[1213];
	unsigned long len = sizeof(buf);

	uncompress(buf, &len, z, sizeof(z));
	return write(STDOUT_FILENO, buf, len) != len;
}

根据笔者丰富的开(瞎)发(猜)经验,这一大串十六进制值一定是某种图像。结合函数名为cup总不能是罩杯吧这肯定是个杯子,没跑了。

再往下面看,感觉很快就能喝到咖啡了:

c
static int fetch_main(void *pctx, struct apk_ctx *ac, struct apk_string_array *args)
{
	struct apk_out *out = &ac->out;
	struct apk_database *db = ac->db;
	struct fetch_ctx *ctx = (struct fetch_ctx *) pctx;
	struct apk_dependency *dep;

	ctx->db = db;
	ctx->prog = db->ctx->progress;
	if (ctx->flags & FETCH_STDOUT) {
		db->ctx->progress.out = 0;
		db->ctx->out.verbosity = 0;
	}

	if (ctx->outdir_fd == 0)
		ctx->outdir_fd = AT_FDCWD;

	if ((args->num == 1) && (strcmp(args->item[0], "coffee") == 0)) {
		if (db->ctx->force) return cup();
		apk_msg(out, "Go and fetch your own coffee.");
		return 0;
	}

	if (ctx->flags & FETCH_RECURSIVE) {
		apk_dependency_array_init(&ctx->world);
		foreach_array_item(dep, db->world)
			mark_dep_flags(ctx, dep);
		if (args->num)
			apk_db_foreach_matching_name(db, args, mark_name_flags, ctx);
		if (ctx->errors == 0)
			mark_names_recursive(db, args, ctx);
		apk_dependency_array_free(&ctx->world);
	} else {
		if (args->num)
			apk_db_foreach_matching_name(db, args, mark_name, ctx);
	}
	if (!ctx->errors)
		apk_db_foreach_sorted_package(db, NULL, fetch_package, ctx);

	/* Remove packages not matching download spec from the output directory */
	if (!ctx->errors && (db->ctx->flags & APK_PURGE) &&
	    !(ctx->flags & FETCH_STDOUT) && ctx->outdir_fd > 0)
		apk_dir_foreach_file(ctx->outdir_fd, purge_package, ctx);

	return ctx->errors;
}

执行apk fetch coffee:

bash
$ apk fetch coffee
Go and fetch your own coffee.

什么,居然要自己泡咖啡,那要你干啥捏,加个-f参数强制泡上一杯:

bash
$ apk fetch -f coffee

                       (
                          )     (
                           ___...(-------)-....___
                       .-""       )    (          ""-.
                 .-'``'|-._             )         _.-|
                /  .--.|   `""---...........---""`   |
               /  /    |                             |
               |  |    |                             |
                \  \   |                             |
                 `\ `\ |                             |
                   `\ `|                             |
                   _/ /\                             /
                  (__/  \                           /
               _..---""` \                         /`""---.._
            .-'           \                       /          '-.
           :               `-.__             __.-'              :
           :                  ) ""---...---"" (                 :
            '._               `"--...___...--"`              _.'
          jgs \""--..__                              __..--""/
               '._     """----.....______.....----"""     _.'
                  `""--..,,_____            _____,,..--""`
                                `"""----"""`

OK,现在可以享受一杯基于musl libc的 coffee 了。