功能丰富的 Perl: Perl 和 Amazon 云,第 4 部分--深入探究完整 mod_perl 站点的代码库

这个共分 5 部分的系列文章向您介绍了如何使用 Perl 和 Apache 构建一个照片共享网站,从而访问 Amazon 的 Simple Storage Service (S3) 和 SimpleDB。在本期文章中,研究完整 mod_perl 站点的代码库,包括如何设置顶级配置、如何使用处理程序,以及如何设置外部依赖关系。

在本期文章中,我们将研究完整的 mod_perl 站点(只讨论代码;模板在下期文章讨论)。前几期文章中,我们的节奏有些缓慢,现在,通过研究 mod_perl,我们将加快步伐。

我强烈建议您阅读源代码。该站点是功能性的,但是许多细节都没有在本系列中详细介绍,我希望您能理解这些细节或者了解还存在疑问的地方。您可以通过书店或使用搜索引擎获取相关的信息。

特别是,设置一个完整的 mod_perl 站点并使用 Template Toolkit 是非常广泛的主题,并且已经介绍过许多次,因此这里不会再解释。最佳学习途径就是了解每一个问题和难点,直到网站可以正常运行。本系列将给出可以使网站正常运行所需的所有内容 — 但是需要由您来将所有内容结合起来。

和此前的文章一样,我将使用 share.lifelogs.com 作为域名。当在您自己的环境中使用时,应该根据需要修改它。

顶级配置

您应当具有一个提供 mod_perl 支持的有效 Apache 服务器。将以下内容插入到 Apache httpd.conf 文件中,如清单 1 所示:

清单 1. 为 Apache 配置文件 share.httpd.conf 提供 mod_perl 支持

<VirtualHost 1.2.3.4:80> ServerName share.lifelogs.com DocumentRoot /var/www/html ErrorLog /var/log/apache/error-share.log PerlRequire /home/tzz/mod_perl_require_share.pl <Location /> SetHandler perl-script PerlHandler SharePerlHandler </Location> SetEnv AWS_KEY 'my-AWS-key' SetEnv AWS_SECRET_KEY 'my-secret-AWS-key'</VirtualHost>

可以看到,所有内容都位于 /home/tzz 中。

下面是需要注意的内容:

有一个特定的错误日志(因此可以独立地观察站点错误) 在过程环境中传递 Amazon 开发人员密匙。这样,Perl 源代码泄漏后就不会丢失它们。(Web 服务器配置通常比源代码更安全)。

注意所有内容 均通过 SharePerlHandler 处理,share.lifelogs.com 上的所有请求!也许您并不希望在生产环境中这样做。

PerlRequire 指令仅仅设置了一个环境,没有做其他特殊的操作。再次强调一下,所有内容都位于 /home/tzz 中。

清单 2 展示了 mod_perl_require_share Perl 文件。

清单 2. mod_perl_require_share.pl 文件

#!/usr/bin/perl -wuse strict;use lib '/home/tzz';use SharePerlHandler;1;

mod_perl 处理程序

mod_perl 处理程序全部位于 SharePerlHandler.pm 文件中。它有多个部分,可大致分为:设置、主处理程序、评论和照片处理程序、通用实用工具和 SimpleDB 实用工具。

通用的和 SimpleDB 实用工具可以拥有自己的模块,但是为了保持简单,我将所有内容都放到了一个位置。评论和照片处理程序以及 SimpleDB 实用工具功能基本上都来自于 simple_go.pl 脚本(见 下载),只进行了少量修改。

让我们首先进行设置。在阅读每一节时,我将解释所做出的决策;当我使用不同的方式进行处理时,最常用的理由就是 “简单性”。打造出色 的网站是一项困难的工作,因此应当将这里学到的所有内容作为一个粗糙的模板,然后根据您的需要和预算进行筛选,而不是将其作为完善的设计直接应用到生产中。提供有效的功能可能会分散注意力,但是我总是忍不住这样做。

设置外部依赖关系

清单 3 展示了 SharePerlHandler.pm 文件的基本设置:

清单 3. SharePerlHandler.pm 文件的基本设置

package SharePerlHandler;use Apache::Constants qw(:common REDIRECT);use strict;use Carp qw/verbose cluck carp croak confess/;use Data::Dumper;use Apache::Request;use Template;use POSIX;use Digest::HMAC_SHA1 qw(hmac_sha1 hmac_sha1_hex);use MIME::Base64;use Data::UUID; # generates unique IDsuse lib '/home/tzz/amazon-simpledb-2007-11-07-perl-library/src/';use Amazon::SimpleDB::Client;

SharePerlHandler.pm 依赖于许多模块。首先,它使用 strict 模块,这是实现良好 Perl 编程的基本要素。在 use strict 下无法运行的内容不会被放入生产环境中。同样:

Carp 模块提供了更好的错误。 Data::Dumper 用于一般调试。 POSIX 用于许多经常使用的函数。 Digest::HMAC_SHA1 和 MIME::Base64 用于 Amazon S3 上传策略。 Template 模块是 Template Toolkit,它使我们能够快速组合 HTML 页面和一些动态内容。 Data::UUID 用于生成惟一的 ID。 Apache::Request 和 Apache::Constants 用于 mod_perl 与 Apache 服务器的交互。 最后,Amazon::SimpleDB::Client 来自 Amazon 并使我们能够与 SimpleDB 交互。

如果不知道如何从 CPAN 安装这些模块,那么使用 cpan -e 'install MODULE' 完成安装。

我们在这里没有使用 Net::Amazon::S3 模块,但是我们本来可以使用的(我在第 2 部分中提到过)。为了保持简单,因此在制定架构决策时决定不使用这个模块;在介绍上传时将详细讨论这点。

清单 4 展示了 SharePerlHandler.pm 的全局设置。

清单 4. SharePerlHandler.pm 的全局设置

use constant IMAGE_MODE => 0;use constant COMMENT_MODE => 1;use constant VERBOSE => 1; # can also be done through the environment or some other waymy $template = Template->new( { INCLUDE_PATH => '/home/tzz/templates/share', RECURSION => 1, } );my $uuid = Data::UUID->new();

您需要使用常量来表示基本 SimpleDB 操作的照片模式和评论模式,因此在此处定义它们。VERBOSE 常量可用任何其他方法代替,以控制服务器的详细记录。记住,对冗详细记录的动态控制越多,它的成本就越高(因为服务器需要每次进行检查)。

接下来,您将获得一个全新的 $templates 对象(将从 /home/tzz/templates/share 加载模板并递归)。最后,您获得一个 UUID 生成器,可以在任意位置使用。

主处理程序

是的,主处理程序非常重要,奇迹将在这里发生。所以,一定要多加注意!(醒来了?很好!)仔细查看清单 5。

清单 5. 更多全局设置

sub handler{ my $r = shift @_; my $q = Apache::Request->new($r, POST_MAX => 4096, DISABLE_UPLOADS => 1); my $user = (rand 2 > 1) ? 'bob' : 'ted'; # pick a user randomly between bob and ted # (50% chance each) handle_photo($q); # always try to delete, add, or edit a URL # if it's passed as a parameter handle_comment($q); # always try to delete, add, or edit a comment # if it's passed as a parameter my $uri = $q->uri(); my $tfile = $uri; $tfile =~ s,^/,,; # remove the starting "/" in the name if it exists $tfile =~ s,/$,,; # remove the ending "/" in the name if it exists $tfile =~ s,/,_,g; # "/" in the file name becomes "_" so all the # templates can be in one directory $tfile = 'index' unless length $tfile; # make the URI "index" if it's # empty (e.g. someone hit the / URI) if ($tfile =~ m/\.html$/) { $tfile =~ s/html$/tmpl/; # map ANYTHING.html to ANYTHING.tmpl } else { $tfile .= '.tmpl'; # map ANYTHING to ANYTHING.tmpl } my $policy = ''; my $signature = ''; if ($tfile eq 'upload.tmpl') { $template->process('policy.tmpl', { username => $user, }, \$policy) || croak($template->error()); my $key = $ENV{AWS_SECRET_KEY}; $policy = encode_base64($policy); $policy =~ s/\n//g; $signature = encode_base64(hmac_sha1($policy, $key)); $signature =~ s/\n//g; } $q->send_http_header('text/html; charset=utf-8'); my $output = ''; $template->process($tfile, { request => $q, username => $user, policy => $policy, signature => $signature, env => \%ENV, params => \%{$q->param()}, fimages => sub { return list_simpledb(sprintf('SELECT * from `%s`', simpledb_image_domain_name())) }, fcomments => \&get_comments, }, \$output) || croak($template->error()); print $output; return OK;}

好长的一个函数,它太长了!如果必须再添加一点逻辑的话,我希望提取出一些独立的片段(即 “重构” 它,就像目前人们常谈论的一样)。但是,它很好地展示了主处理程序。

首先,函数获得请求对象。然后它设置一个随机用户名(“bob” 或 “ted”);通常您可以按照自己的方式设定,比如通过 cookie,或者可以让 Apache 替您处理身份验证和授权。

我在功能丰富的 Perl:Perl 和 Amazon 云,第 1 部分说过,我将在 SimpleDB 中使用一个用户表,但是它会使站点变得非常复杂,因此放弃了这个想法。在 SimpleDB 中查看用户并不容易,因为我需要提供一种方法来登录并管理用户信息。这会使代码变得非常长,因此忘记这个想法吧 — 但是这个想法绝对是可行的。

接下来您将处理任意照片或评论参数。例如,如果发现 deletecommentid 参数,那么尝试删除这个评论 ID。我们稍后将详细探讨照片和评论参数处理程序。

接下来需要管理实际的请求。这可以通过简单的映射完成,将 “any/request/here.html” 转换为 “any_request_here.tmpl” 并请求该模板。我们确保 index.tmpl 用于 “/” 请求。

任何不具备相应模板的 URI 将不会生成数据,实际上会抛出一个错误。这并不是一种适合用于生产 的技巧,但是它只用了几行代码就可以设置一个 Web 站点,因此如果演示的目标是实现简洁性和简单性,那么这个技巧非常有用。

接着,如果模板文件为 “upload.tmpl”,那么需要为 S3 生成一个上传策略,因此可以使用 policy.tmpl 文件实现此目的。用户名被传递到该模板,非常类似于本系列 第 2 部分中的模板。清单 6 展示了一个策略模板。

清单 6. 最大宽度的样例代码清单

{"expiration": "3000-01-01T00:00:00Z", "conditions": [ {"bucket": "images.share.lifelogs.com"}, {"acl": "public-read"}, ["starts-with", "$key", ""], ["starts-with", "$Content-Type", ""], ["starts-with", "$success_action_redirect", "http://share.lifelogs.com/s3uploaded?user=[% username %]"], ["content-length-range", 0, 1048576] ]}

这里的主要区别在于没有对成功的 URL 使用用户名,而是将用户名作为参数,因为它可以使照片参数处理程序变得更加简单。稍后将详细讨论。

现在您对策略进行了签名并准备发送 HTTP 头部(Apache 为我们完成!)。接下来,使用一些参数生成必要的输出,如下所示:

request,Apache 请求 username,随机用户名 policy,S3 上传策略(可以为空) signature,S3 策略签名(可以为空) env。进程环境(不要应用于生产中!) params,参数,例如来自 POST 或 GET 请求 fimages,返回所有照片的函数 fcomments,根据照片 ID 返回所有评论的函数

这就是通用处理程序的内容。所有其他神奇的地方则发生在评论和照片参数处理程序以及参数本身中。让我们继续研究下去。

照片和评论处理程序将针对每个请求进行调用。如果它们发现参数是正确的,那么就将执行下面的操作:添加、修改或删除照片或评论。模板(我们将在 SharePerlHandler.pm 之后探讨)在 POST 表单中包含这些参数。

但是不要着急,还有一些有趣的内容 — 伪劣的代码、糟糕的架构,您希望发现一个值得发布到 Twitter 上的 bug(“haha @tzlatanov sux teh worst 将检查在无效上下文中使用的 map,而 omg b0rken 模板 obv 并不是真正的 h4ck3r mod_perl issolastmllnm”)。

评论参数处理程序

清单 7 展示了评论参数处理程序。

清单 7. 评论参数处理程序

sub handle_comment{ my $q = shift @_; my $user = $q->param('user'); my $imageid = $q->param('refimageid'); my $comment = $q->param('comment'); my $refcommentid = $q->param('refcommentid'); my $commentid = $q->param('commentid'); my $deleteid = $q->param('deletecommentid'); my $result; if (defined $deleteid) # delete { $result = delete_simpledb($deleteid, COMMENT_MODE); } elsif (defined $commentid && defined $comment) # edit { my %q = ( comment => $comment, ); put_simpledb($commentid, COMMENT_MODE, %q); $result = get_simpledb($commentid, COMMENT_MODE); } elsif (defined $imageid && defined $comment) # new comment { my %q = ( image_id => $imageid, comment => $comment, ); $q{reply_to} = $refcommentid if defined $refcommentid; $q{user} = $user if defined $user; my $id = new_uuid(); put_simpledb($id, COMMENT_MODE, %q); $result = get_simpledb($id, COMMENT_MODE); } $q->param()->{'result'} = $result;}

根据传入的参数,评论参数处理程序有三个可能的模式。模式都是互相排斥的。在所有情况下,result 查询参数被相应地设置为表示成功(如果被定义的话)。因此,模块可以稍后检查是否设置了该参数以及是否正确使用。当站点不断变大时,很可能需要添加其他参数,例如 last_operation 或 error_message。

如果设置了 deletecommentid 参数,处理程序将使用合适的值调用 delete_simpledb。这是最简单的、没有任何限制的模式。

第二个模式修改评论。它不会检查评论是否已经存在,因此错误的 ID 会在此创建一个新评论。要进行检查也很简单,但是需要付出开销(您必须额外调用 SimpleDB,而调用会很慢,因为需要完整的 HTTP 往返以及 Amazon 端的处理时间)。

注意,由于每个评论有一个自己的 ID,因此编辑起来很容易。可以不使用单个评论记录,而是将分组评论作为照片属性(字符串数组,每个评论使用一个),但是这样的话就很难编辑或删除单个评论。实际上,您必须在评论内实现自己的记录结构来表示 ID、一个发贴用户,等等。

编辑模式可以通过 commentid 和 comment 查询参数触发,这两个参数分别表示标记模板的 UUID 和评论的新内容。结果是从 SimpleDB 检索相同 UUID,因此可以在这里进行检查,看看返回的评论字段是否是您所希望的(如果不是则说明出现了错误)。为了保证简单,我在此并没有执行检查。

最后一种模式,创建一个新评论,由 imageid 和 comment 参数触发。可选地,它将使用一个引用 UUID(如果评论是另一个评论的回复的话)和一个用户名(如果评论不是匿名的话)。和编辑模式一样,只需对属性执行 put_simpledb 并将它们返回,而不用检查字段是否被正确修改。

照片参数处理程序

这个处理程序非常类似于评论处理程序,因此如果刚才睡着了的话,那么返回去再看一遍。

照片 URL(如果没有传递的话)是由 S3 键和 bucket 构造的。这样您就具有一个一致的接口来在 S3 上传后处理成功的重定向。清单 8 展示了照片参数处理程序。

清单 8. 照片参数处理程序

sub handle_photo{ my $q = shift @_; my $user = $q->param('user'); my $name = $q->param('name'); my $bucket = $q->param('bucket'); my $key = $q->param('key'); my $url = $q->param('url'); my $editid = $q->param('imageid'); my $deleteid = $q->param('deleteimageid'); # set the URL from the S3 key and bucket if necessary if (!defined $url && defined $key && defined $bucket) { $url = sprintf("http://%s.s3.amazonaws.com/%s", $bucket, $key) } my $result; if (defined $deleteid) # delete { $result = delete_simpledb($deleteid, IMAGE_MODE); } elsif (defined $name && defined $editid) # editing an image name { my %q = ( name => $name, ); put_simpledb($editid, IMAGE_MODE, %q); $result = get_simpledb($editid, IMAGE_MODE); } elsif (defined $url && defined $name && defined $user) # adding a new one { my %q = ( url => $url, name => $name, user => $user, ); $q{bucket} = $bucket if defined $bucket; my $id = new_uuid(); put_simpledb($id, IMAGE_MODE, %q); $result = get_simpledb($id, IMAGE_MODE); } $q->param()->{'result'} = $result;}

删除照片的操作是由 deleteimageid 参数触发的。编辑照片名称可以通过 name 和 imageid 参数完成。创建新照片可以通过 URL、用户名和照片名完成。照片 bucket 是可选的,并且只有当处理程序调用发生在 S3 上传成功重定向到站点之后才会显示。

回忆一下策略,S3 成功重定向使用用户名作为 URL 参数的一部分。它也包含键和 bucket,因此您只需要从中创建一个照片。

实用函数

让我们看一下各种实用函数。

清单 9. 各种实用函数

sub qlog{ printf STDERR @_; print STDERR "\n";}sub new_uuid{ return $uuid->to_string($uuid->create());}sub simpledb_image_domain_name{ return simpledb_domain_name(IMAGE_MODE);}sub simpledb_comment_domain_name{ return simpledb_domain_name(COMMENT_MODE);}sub simpledb_domain_name{ return sprintf "%s.share.lifelogs.com", (shift == IMAGE_MODE) ? 'share_photos' : 'share_comments';}

如果不希望每次编写 printf STDERR,那么 qlog 将非常有用。它还为您编写了行结束符。是否要将各种输出写入到 Apache 错误日志,这由您决定。同样:

new_uuid 用于生成新的 UUID。 simpledb_domain_name、simpledb_image_domain_name 和 simpledb_comment_domain_name 使用模式参数(可以是 IMAGE_MODE 或 COMMENT_MODE)来提供一个 SimpleDB 域。

SimpleDB 实用函数

SimpleDB 实用函数几乎和第 3 部分中的函数(simple_go.pl)完全相同。可从下面的下载小节下载。我将列出其中的不同:

get_comments 是一个新函数,用来获得全部评论,按照片 ID 编排,然后按照父评论 ID 或 noparent 显示。 qlog 替代了 print,而 VERBOSE 常量替代了 $verbose。 使用环境 AWS_KEY 和 AWS_SECRET_KEY 键为每个请求初始化服务。 传递 $mode 模式,而不是用于全局。 对于错误,没有使用 die(),您将尽可能优雅地处理它们。 域名通过 simpledb_domain_name 获得,而不是使用全局变量。 函数被重命名,因为 “get” 和 “put” 在多用途名称空间中不算好名字,就像旧的 simple_go.pl 脚本那样。

再次提醒一下,这段代码只针对每个属性使用一个值。如果任何属性使用了一个字符串数组,那么将只返回其中的一个字符串。在编写完属性后,只会留下一个值,即使之前有一组值。这大大简化了代码,但并不适合用于通用的 SimpleDB。

结束语

通过本系列第 2 部分和第 3 部分的介绍,现在您已经从总体上了解了完整的 mod_perl 站点的代码(这两篇文章的下载文件也可以从下面的下载小节获得)。最终生成的站点使用 Template Toolkit、S3 和 SimpleDB 来提供照片上传、主题浏览、添加评论、编辑和删除功能。

在第 5 部分中,我们将研究这个站点的模板。

SimpleDB 实用函数,simpledb_utility.zip:temp_10030721531631.zip

样例脚本(来自第 2 部分),s3form.zip:temp_10030721549116.zip

样例脚本(来自第 3 部分),simple_go.zip:temp_10030721552175.zip

时间: 2024-08-20 13:29:39

功能丰富的 Perl: Perl 和 Amazon 云,第 4 部分--深入探究完整 mod_perl 站点的代码库的相关文章

功能丰富的 Perl: Perl 和 Amazon 云,第 5 部分--了解完整 mod_perl 站点的模板

这个共分 5 部分的系列文章向您介绍了如何使用 Perl 和 Apache 构建一个照片共享网站,从而访问 Amazon 的 Simple Storage Service (S3) 和 SimpleDB.在这最后一期文章中,我们将考察完整 mod_perl 站点的模板,包括一个用于索引的模板.三个用于上传的模板(通用模板.S3 表单和 URL 添加物).一个用于浏览照片和评论的模板,以及一个用于递归式地浏览(即遍历浏览)照片评论的模板. 在最后一期文章中,我们将了解完整的 mod_perl 站点

Amazon云基础设施采用英特尔芯片

Amazon公司坦言称,目前其云基础设施使用的正是英特尔的芯片.这一声明解开了长久以来困扰技术行业的猜测与疑惑. 这条声明来自本周二于旧金山召开IDF(Intel Developer Forum),此次Amazon史无前例地就其云基础设施处理器问题大谈特谈,并从细节角度重点强调了英特尔产品的卓越功能. Amazon表示引入英特尔芯片的做法是为了满足客户需求,而英特尔方面则指出这项协议是为了推动Amazon云的品牌号召力更上一层楼.不过面对我们提出的问题--AWS对芯片产品的批量性采购是否能获得来

报告称,Amazon云服务成为史上增长最快的企业级

摘要: 美国投行太平洋皇冠证券(Pacific Crest Securities)的最新数据显示, 2014年,Amazon云计算服务(Amazon Web Services, AWS)截至目前的收入已经达到了50亿美元. 这意味着Amazon来自其云计算服务 美国投行太平洋皇冠证券(Pacific Crest Securities)的最新数据显示,2014年,Amazon云计算服务(Amazon Web Services, AWS)截至目前的收入已经达到了50亿美元.这意味着Amazon来自其

新浪云平台如何发布java web项目,怎么部署代码和环境、数据库?

问题描述 新浪云平台如何发布java web项目,怎么部署代码和环境.数据库? 新浪云平台如何发布java web项目,怎么部署代码和环境,需要什么权限,搭建完环境后就可以在平台上写代码了吗? 解决方案 没用过!不过阿里云在用,你就是远程连上自己配置啊!和你本地差不多的! 解决方案二: ibm云在搞免费 活动http://blog.csdn.net/evankaka/article/details/49836551 可以试试

功能丰富的 Perl: Perl 和 Amazon 云,第 3 部分--上传图像并创建、编辑和删除评论

这个共分 5 部分的系列文章向您介绍了如何使用 Perl 和 Apache 访问 Amazon 的 Simple Storage Service (S3) 和 SimpleDB,从而构建一个照片共享网站.在本期文章中,通过学习 URL 如何为上传的文件创建 SimpleDB 记录,实现站点与 SimpleDB 的交互.同时了解如何以 SimpleDB 记录的形式创建.编辑.删除某个用户的照片评论. 距离我的上一期文章已经有一段时间了,现在我们来回顾一下: 第 1 部分解释了 S3/SimpleD

功能丰富的 Perl: Perl 和 Amazon 云,第 1 部分--通过构建简单的照片共享网站学习 Amazon S3 和 SimpleDB 服务的基础知识

在这个 5 部分系列文章的第 1 部分中,Ted Zlatanov 解释了 Amazon 的 Simple Storage Service (S3) 和 SimpleDB 产品在构建 Web 站点方面的优势和不足.这个系列通过实际示例形象地介绍了 S3 和 SimpleDB 架构以及如何使用它们:在本文中,将使用 Perl 库构建一个简单的照片共享网站. 您需要学习 Amazon 的两个 Web 服务:Amazon S3 (Simple Storage Service) 和 Amazon Sim

功能丰富的 Perl: Perl 和 Amazon 云,第 2 部分--通过 HTML 表单将数据安全地上传到 S3

这个共5个部分的系列文章将带领您使用 Perl 和 Apache 构建一个简单的照片共享网站,访问 Amazon 的 Simple Storage Service (S3) 和 SimpleDB.在这一期文章中,理解如何通过一个 HTML 表单将一个文件从 Web 页面上传到 S3,最小化服务上的负载,同时维护严格的安全策略. 您可以通过多种方式将文件从一个 Web 页面上传到 Amazon 的 Simple Storage Service (S3): 使用来自 CPAN 的合适的模块从命令行上

教程:如何将Linux应用程序迁移到Amazon云

基础设施即服务(Infrastructure as a Service,IaaS)是一个极好的概念:您使用计算资源并支付费用.需要的资源越多,支付的费用也就越高.这个模式的不足之处在于,您看不到正在使用的计算机,您对那些计算机真的知之甚少.但一旦克服这一点,使用 IaaS 可以带来很多好处. 由于 IaaS 模式与传统的购买服务器的模式差别很大,因此您管理您的虚拟计算机的方法也随之发生改变.这还意味着,您在云中运行应用程序的方式也发生了改变.以前想当然的东西,比如服务器之间无足轻重的延迟,现在不

perl与shell获取昨天、明天或多天前的日期的代码_perl

一.首先说下perl如何获取 复制代码 代码如下: #!/usr/bin/perl -wuse POSIX qw(strftime);my $day = strftime("%Y%m%d", localtime(time - 24*3600));#获取昨天的日期 二.在Linux中对man date -d 参数说的比拟笼统,以下示例进一步解释: # -d, --date=STRING display time described by STRING, not `now'[root@Gm