]> granicus.if.org Git - php/commitdiff
- Fixed #52691 (allow multiple instance of FPM using a custom prefix)
authorJérôme Loyet <fat@php.net>
Sun, 14 Nov 2010 22:01:34 +0000 (22:01 +0000)
committerJérôme Loyet <fat@php.net>
Sun, 14 Nov 2010 22:01:34 +0000 (22:01 +0000)
sapi/fpm/config.m4
sapi/fpm/fpm/fpm.c
sapi/fpm/fpm/fpm.h
sapi/fpm/fpm/fpm_conf.c
sapi/fpm/fpm/fpm_conf.h
sapi/fpm/fpm/fpm_main.c
sapi/fpm/fpm/fpm_php.h
sapi/fpm/php-fpm.8.in
sapi/fpm/php-fpm.conf.in

index f2273be020675bd8e087e7048738d5ba5618fcd2..0c8271101b027e424e00a389158cc43448b194b7 100644 (file)
@@ -603,6 +603,8 @@ if test "$PHP_FPM" != "no"; then
   PHP_SUBST_OLD(php_fpm_sysconfdir)
   php_fpm_localstatedir=`eval echo $localstatedir`
   PHP_SUBST_OLD(php_fpm_localstatedir)
+  php_fpm_prefix=`eval echo $prefix`
+  PHP_SUBST_OLD(php_fpm_prefix)
 
   AC_DEFINE_UNQUOTED(PHP_FPM_USER, "$php_fpm_user", [fpm user name])
   AC_DEFINE_UNQUOTED(PHP_FPM_GROUP, "$php_fpm_group", [fpm group name])
index 871d7645468bc2d45993b6550c3e27e14a124edc..eb1ff40197b5dacf7bb02a3e139c0c08dce1f31d 100644 (file)
 
 struct fpm_globals_s fpm_globals;
 
-int fpm_init(int argc, char **argv, char *config, struct event_base **base) /* {{{ */
+int fpm_init(int argc, char **argv, char *config, char *prefix, struct event_base **base) /* {{{ */
 {
        fpm_globals.argc = argc;
        fpm_globals.argv = argv;
        fpm_globals.config = config;
+       fpm_globals.prefix = prefix;
 
        if (0 > fpm_php_init_main()            ||
                0 > fpm_stdio_init_main()            ||
index 626afbe0e9e64a4563163c46f4d789b6666d1241..b0d8e1369964bfabc7f4d62a222aa0f16ec2c70e 100644 (file)
 #include <event.h>
 
 int fpm_run(int *max_requests, struct event_base *base);
-int fpm_init(int argc, char **argv, char *config, struct event_base **base);
+int fpm_init(int argc, char **argv, char *config, char *prefix, struct event_base **base);
 
 struct fpm_globals_s {
        pid_t parent_pid;
        int argc;
        char **argv;
        char *config;
+       char *prefix;
        int running_children;
        int error_log_fd;
        int log_level;
index ff0ae7fcdbef983b5f214922266982081025ae9a..2c49410e20f111a4bfeb0de958615dcfadb45bf5 100644 (file)
@@ -74,6 +74,7 @@ static struct ini_value_parser_s ini_fpm_global_options[] = {
 };
 
 static struct ini_value_parser_s ini_fpm_pool_options[] = {
+       { "prefix", &fpm_conf_set_string, WPO(prefix) },
        { "user", &fpm_conf_set_string, WPO(user) },
        { "group", &fpm_conf_set_string, WPO(group) },
        { "chroot", &fpm_conf_set_string, WPO(chroot) },
@@ -114,6 +115,29 @@ static int fpm_conf_is_dir(char *path) /* {{{ */
 }
 /* }}} */
 
+static int fpm_conf_expand_pool_name(char **value) {
+       char *token;
+
+       if (!value || !*value) {
+               return 0;
+       }
+
+       while ((token = strstr(*value, "$pool"))) {
+               char *buf;
+               char *p1 = *value;
+               char *p2 = token + strlen("$pool");
+               if (!current_wp || !current_wp->config  || !current_wp->config->name) {
+                       return -1;
+               }
+               token[0] = '\0';
+               spprintf(&buf, 0, "%s%s%s", p1, current_wp->config->name, p2);
+               *value = strdup(buf);
+               efree(buf);
+       }
+
+       return 0;
+}
+
 static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset) /* {{{ */
 {
        char *val = Z_STRVAL_P(value);
@@ -141,6 +165,9 @@ static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset) /*
        if (!new) {
                return "fpm_conf_set_string(): strdup() failed";
        }
+       if (fpm_conf_expand_pool_name(&new) == -1) {
+               return "Can't use '$pool' when the pool is not defined";
+       }
 
        *old = new;
        return NULL;
@@ -295,6 +322,9 @@ static char *fpm_conf_set_array(zval *key, zval *value, void **config, int conve
                kv->value = strdup(b ? "On" : "Off");
        } else {
                kv->value = strdup(Z_STRVAL_P(value));
+               if (fpm_conf_expand_pool_name(&kv->value) == -1) {
+                       return "Can't use '$pool' when the pool is not defined";
+               }
        }
 
        if (!kv->value) {
@@ -381,27 +411,68 @@ int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */
        free(wpc->chroot);
        free(wpc->chdir);
        free(wpc->slowlog);
+       free(wpc->prefix);
 
        return 0;
 }
 /* }}} */
 
-static int fpm_evaluate_full_path(char **path) /* {{{ */
+static int fpm_evaluate_full_path(char **path, struct fpm_worker_pool_s *wp, char *default_prefix, int expand) /* {{{ */
 {
-       if (**path != '/') {
-               char *full_path;
+       char *prefix = NULL;
+       char *full_path;
 
-               full_path = malloc(sizeof(PHP_PREFIX) + strlen(*path) + 1);
+       if (!path || !*path || **path == '/') {
+               return 0;
+       }
 
-               if (!full_path) { 
-                       return -1;
+       if (wp && wp->config) {
+               prefix = wp->config->prefix;
+       }
+
+       /* if the wp prefix is not set */
+       if (prefix == NULL) {
+               prefix = fpm_globals.prefix;
+       }
+
+       /* if the global prefix is not set */
+       if (prefix == NULL) {
+               prefix = default_prefix ? default_prefix : PHP_PREFIX;
+       }
+
+       if (expand) {
+               char *tmp;
+               tmp = strstr(*path, "$prefix");
+               if (tmp != NULL) {
+
+                       if (tmp != *path) {
+                               zlog(ZLOG_ERROR, "'$prefix' must be use at the begining of the value");
+                               return -1;
+                       }
+
+                       if (strlen(*path) > strlen("$prefix")) {
+                               free(*path);
+                               tmp = strdup((*path) + strlen("$prefix"));
+                               *path = tmp;
+                       } else {
+                               free(*path);
+                               *path = NULL;
+                       }
                }
+       }
 
-               sprintf(full_path, "%s/%s", PHP_PREFIX, *path);
+       if (*path) {
+               spprintf(&full_path, 0, "%s/%s", prefix, *path);
                free(*path);
-               *path = full_path;
+               *path = strdup(full_path);
+               efree(full_path);
+       } else {
+               *path = strdup(prefix);
        }
 
+       if (**path != '/' && wp != NULL && wp->config) {
+               return fpm_evaluate_full_path(path, NULL, default_prefix, expand);
+       }
        return 0;
 }
 /* }}} */
@@ -417,11 +488,20 @@ static int fpm_conf_process_all_pools() /* {{{ */
 
        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
 
+               if (wp->config->prefix && *wp->config->prefix) {
+                       fpm_evaluate_full_path(&wp->config->prefix, NULL, NULL, 0);
+
+                       if (!fpm_conf_is_dir(wp->config->prefix)) {
+                               zlog(ZLOG_ERROR, "[pool %s] the prefix '%s' does not exist or is not a directory", wp->config->name, wp->config->prefix);
+                               return -1;
+                       }
+               }
+
                if (wp->config->listen_address && *wp->config->listen_address) {
                        wp->listen_address_domain = fpm_sockets_domain_from_address(wp->config->listen_address);
 
                        if (wp->listen_address_domain == FPM_AF_UNIX && *wp->config->listen_address != '/') {
-                               fpm_evaluate_full_path(&wp->config->listen_address);
+                               fpm_evaluate_full_path(&wp->config->listen_address, wp, NULL, 0);
                        }
                } else {
                        zlog(ZLOG_ALERT, "[pool %s] no listen address have been defined!", wp->config->name);
@@ -478,6 +558,9 @@ static int fpm_conf_process_all_pools() /* {{{ */
 
                }
 
+               if (wp->config->slowlog && *wp->config->slowlog) {
+                       fpm_evaluate_full_path(&wp->config->slowlog, wp, NULL, 0);
+               }
 
                if (wp->config->request_slowlog_timeout) {
 #if HAVE_FPM_TRACE
@@ -495,14 +578,10 @@ static int fpm_conf_process_all_pools() /* {{{ */
 
                        wp->config->request_slowlog_timeout = 0;
 #endif
-               }
-
-               if (wp->config->request_slowlog_timeout && wp->config->slowlog && *wp->config->slowlog) {
-                       int fd;
 
-                       fpm_evaluate_full_path(&wp->config->slowlog);
+                       if (wp->config->slowlog && *wp->config->slowlog) {
+                               int fd;
 
-                       if (wp->config->request_slowlog_timeout) {
                                fd = open(wp->config->slowlog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
 
                                if (0 > fd) {
@@ -583,6 +662,9 @@ static int fpm_conf_process_all_pools() /* {{{ */
                }
 
                if (wp->config->chroot && *wp->config->chroot) {
+
+                       fpm_evaluate_full_path(&wp->config->chroot, wp, NULL, 1);
+
                        if (*wp->config->chroot != '/') {
                                zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' must start with a '/'", wp->config->name, wp->config->chroot);
                                return -1;
@@ -594,6 +676,9 @@ static int fpm_conf_process_all_pools() /* {{{ */
                }
 
                if (wp->config->chdir && *wp->config->chdir) {
+
+                       fpm_evaluate_full_path(&wp->config->chdir, wp, NULL, 0);
+
                        if (*wp->config->chdir != '/') {
                                zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' must start with a '/'", wp->config->name, wp->config->chdir);
                                return -1;
@@ -601,21 +686,16 @@ static int fpm_conf_process_all_pools() /* {{{ */
 
                        if (wp->config->chroot) {
                                char *buf;
-                               size_t len;
 
-                               len = strlen(wp->config->chroot) + strlen(wp->config->chdir) + 1;
-                               buf = malloc(sizeof(char) * len);
-                               if (!buf) {
-                                       zlog(ZLOG_SYSERROR, "[pool %s] malloc() failed", wp->config->name);
-                                       return -1;
-                               }
-                               snprintf(buf, len, "%s%s", wp->config->chroot, wp->config->chdir);
+                               spprintf(&buf, 0, "%s/%s", wp->config->chroot, wp->config->chdir);
+
                                if (!fpm_conf_is_dir(buf)) {
                                        zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' within the chroot path '%s' ('%s') does not exist or is not a directory", wp->config->name, wp->config->chdir, wp->config->chroot, buf);
-                                       free(buf);
+                                       efree(buf);
                                        return -1;
                                }
-                               free(buf);
+
+                               efree(buf);
                        } else {
                                if (!fpm_conf_is_dir(wp->config->chdir)) {
                                        zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' does not exist or is not a directory", wp->config->name, wp->config->chdir);
@@ -623,6 +703,26 @@ static int fpm_conf_process_all_pools() /* {{{ */
                                }
                        }
                }
+               if (!wp->config->chroot) {
+                       struct key_value_s *kv;
+                       char *options[] = FPM_PHP_INI_TO_EXPAND;
+                       char **p;
+
+                       for (kv = wp->config->php_values; kv; kv = kv->next) {
+                               for (p=options; *p; p++) {
+                                       if (!strcasecmp(kv->key, *p)) {
+                                               fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
+                                       }
+                               }
+                       }
+                       for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
+                               for (p=options; *p; p++) {
+                                       if (!strcasecmp(kv->key, *p)) {
+                                               fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
+                                       }
+                               }
+                       }
+               }
        }
        return 0;
 }
@@ -671,18 +771,14 @@ int fpm_conf_write_pid() /* {{{ */
 static int fpm_conf_post_process() /* {{{ */
 {
        if (fpm_global_config.pid_file) {
-               fpm_evaluate_full_path(&fpm_global_config.pid_file);
+               fpm_evaluate_full_path(&fpm_global_config.pid_file, NULL, PHP_LOCALSTATEDIR, 0);
        }
 
        if (!fpm_global_config.error_log) {
-               char *tmp_log_path;
-
-               spprintf(&tmp_log_path, 0, "%s/log/php-fpm.log", PHP_LOCALSTATEDIR);
-               fpm_global_config.error_log = strdup(tmp_log_path);
-               efree(tmp_log_path);
+               fpm_global_config.error_log = strdup("log/php-fpm.log");
        }
 
-       fpm_evaluate_full_path(&fpm_global_config.error_log);
+       fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0);
 
        if (0 > fpm_stdio_open_error_log(0)) {
                return -1;
@@ -990,7 +1086,7 @@ int fpm_conf_load_ini_file(char *filename TSRMLS_DC) /* {{{ */
                if (ini_include) {
                        char *tmp = ini_include;
                        ini_include = NULL;
-                       fpm_evaluate_full_path(&tmp);
+                       fpm_evaluate_full_path(&tmp, NULL, NULL, 0);
                        fpm_conf_ini_parser_include(tmp, &error TSRMLS_CC);
                        if (error) {
                                free(tmp);
@@ -1014,10 +1110,23 @@ int fpm_conf_init_main() /* {{{ */
        int ret;
        TSRMLS_FETCH();
 
+       if (fpm_globals.prefix && *fpm_globals.prefix) {
+               if (!fpm_conf_is_dir(fpm_globals.prefix)) {
+                       zlog(ZLOG_ERROR, "the global prefix '%s' does not exist or is not a directory", fpm_globals.prefix);
+                       return -1;
+               }
+       }
+
        if (fpm_globals.config == NULL) {
-               spprintf(&fpm_globals.config, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR);
+
+               if (fpm_globals.prefix == NULL) {
+                       spprintf(&fpm_globals.config, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR);
+               } else {
+                       spprintf(&fpm_globals.config, 0, "%s/etc/php-fpm.conf", fpm_globals.prefix);
+               }
+
                if (!fpm_globals.config) {
-                       zlog(ZLOG_SYSERROR, "spprintf() failed (\"%s/php-fpm.conf\")", PHP_SYSCONFDIR);
+                       zlog(ZLOG_SYSERROR, "spprintf() failed (fpm_globals.config)");
                        return -1;
                }
        }
index aace7811ebbce7666f26be4c6ce13d13bc019d2c..2e65efe2eabd1f0e99f5106059e399c01e689eb1 100644 (file)
@@ -31,6 +31,7 @@ extern struct fpm_global_config_s fpm_global_config;
 
 struct fpm_worker_pool_config_s {
        char *name;
+       char *prefix;
        char *user;
        char *group;
        char *chroot;
index 8086a7bdfd7b1813dc1e13af486f3fff43dbb869..b254a765cd2e1fdb3d9346ce0d74800cc97878ff 100644 (file)
@@ -153,6 +153,7 @@ static const opt_struct OPTIONS[] = {
        {'v', 0, "version"},
        {'y', 1, "fpm-config"},
        {'t', 0, "test"},
+       {'p', 1, "prefix"},
        {'-', 0, NULL} /* end of args */
 };
 
@@ -958,7 +959,7 @@ static void php_cgi_usage(char *argv0)
                prog = "php";
        }
 
-       php_printf(     "Usage: %s [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-c <file>] [-d foo[=bar]] [-y <file>]\n"
+       php_printf(     "Usage: %s [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-p <prefix> ] [-c <file>] [-d foo[=bar]] [-y <file>]\n"
                                "  -c <path>|<file> Look for php.ini file in this directory\n"
                                "  -n               No php.ini file will be used\n"
                                "  -d foo[=bar]     Define INI entry foo with value 'bar'\n"
@@ -967,10 +968,12 @@ static void php_cgi_usage(char *argv0)
                                "  -i               PHP information\n"
                                "  -m               Show compiled in modules\n"
                                "  -v               Version number\n"
+                               "  -p, --prefix <dir>\n"
+                               "                   Specify alternative prefix path to FastCGI process manager (default: %s).\n"
                                "  -y, --fpm-config <file>\n"
                                "                   Specify alternative path to FastCGI process manager config file.\n"
                                "  -t, --test       Test FPM configuration and exit\n",
-                               prog);
+                               prog, PHP_PREFIX);
 }
 /* }}} */
 
@@ -1547,6 +1550,7 @@ int main(int argc, char *argv[])
        int fcgi_fd = 0;
        fcgi_request request;
        char *fpm_config = NULL;
+       char *fpm_prefix = NULL;
 
        fcgi_init();
 
@@ -1585,9 +1589,11 @@ int main(int argc, char *argv[])
                                }
                                cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
                                break;
+
                        case 'n':
                                cgi_sapi_module.php_ini_ignore = 1;
                                break;
+
                        case 'd': {
                                /* define ini entries on command line */
                                int len = strlen(php_optarg);
@@ -1619,10 +1625,15 @@ int main(int argc, char *argv[])
                                }
                                break;
                        }
+
                        case 'y':
                                fpm_config = php_optarg;
                                break;
 
+                       case 'p':
+                               fpm_prefix = php_optarg;
+                               break;
+
                        case 'e': /* enable extended info output */
                                CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
                                break;
@@ -1767,7 +1778,7 @@ consult the installation file that came with this distribution, or visit \n\
                }
        }
 
-       if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), &CGIG(event_base))) {
+       if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), fpm_prefix, &CGIG(event_base))) {
                return FAILURE;
        }
 
index 8c4b58ceb6b8210133a1c0c26985e045715a3d59..81e5332671f0dfc0335334dfc429438619522b34 100644 (file)
 #include "build-defs.h" /* for PHP_ defines */
 #include "fpm/fpm_conf.h"
 
+#define FPM_PHP_INI_TO_EXPAND \
+       { \
+               "error_log", \
+               "extension_dir", \
+               "mime_magic.magicfile", \
+               "sendmail_path", \
+               "session.cookie_path", \
+               "session_pgsql.sem_file_name", \
+               "soap.wsdl_cache_dir", \
+               "uploadprogress.file.filename_template", \
+               "xdebug.output_dir", \
+               "xdebug.profiler_output_dir", \
+               "xdebug.trace_output_dir", \
+               "xmms.path", \
+               "axis2.client_home", \
+               "blenc.key_file", \
+               "coin_acceptor.device", \
+               NULL \
+       }
+
 struct fpm_worker_pool_s;
 
 int fpm_php_init_child(struct fpm_worker_pool_s *wp);
index 1aa8b62b95efc9937502d74133f4fbbca29a55cf..3d9dc0981c4aad9d58d689ef254c72483f5632a3 100644 (file)
@@ -77,6 +77,11 @@ Show compiled in modules
 .PD 1
 .B \-v
 Version number
+.B \-\-prefix \fIpath\fP
+.TP
+.PD 1
+.B \-p
+Specify alternative prefix path (the default is @php_fpm_prefix@)
 .TP
 .PD 0
 .B \-\-fpm\-config \fIfile\fP
index ccf864a5e80594d2f46c3f74cd8cccdbe293c18c..bceef97e1cb3a113ba5a7fd94e6553758781b3c6 100644 (file)
@@ -3,12 +3,16 @@
 ;;;;;;;;;;;;;;;;;;;;;
 
 ; All relative paths in this configuration file are relative to PHP's install
-; prefix.
+; prefix (@prefix@). This prefix can be dynamicaly changed by using the
+; '-p' argument from the command line.
 
 ; Include one or more files. If glob(3) exists, it is used to include a bunch of
 ; files from a glob(3) pattern. This directive can be used everywhere in the
 ; file.
-;include=@EXPANDED_SYSCONFDIR@/fpm.d/*.conf
+; Relative path can also be used. They will be prefixed by:
+;  - the global prefix if it's been set (-p arguement)
+;  - @prefix@ otherwise
+;include=etc/fpm.d/*.conf
 
 ;;;;;;;;;;;;;;;;;;
 ; Global Options ;
 
 [global]
 ; Pid file
+; Note: the default prefix is @EXPANDED_LOCALSTATEDIR@
 ; Default Value: none
-;pid = @EXPANDED_LOCALSTATEDIR@/run/php-fpm.pid
+;pid = run/php-fpm.pid
 
 ; Error log file
-; Default Value: @EXPANDED_LOCALSTATEDIR@/log/php-fpm.log
-;error_log = @EXPANDED_LOCALSTATEDIR@/log/php-fpm.log
+; Note: the default prefix is @EXPANDED_LOCALSTATEDIR@
+; Default Value: log/php-fpm.log
+;error_log = log/php-fpm.log
 
 ; Log level
 ; Possible Values: alert, error, warning, notice, debug
 ; FPM can handle. Your system will tell you anyway :)
 
 ; Start a new pool named 'www'.
+; the variable $pool can we used in any directive and will be replaced by the
+; pool name ('www' here)
 [www]
 
+; Per pool prefix
+; It only applies on the following directives:
+; - 'slowlog'
+; - 'listen' (unixsocket)
+; - 'chroot'
+; - 'chdir'
+; - 'php_values'
+; - 'php_admin_values'
+; When not set, the global prefix (or @php_fpm_prefix@) applies instead.
+; Note: This directive can also be relative to the global prefix.
+; Default Value: none
+;prefix = /path/to/pools/$pool
+
 ; The address on which to accept FastCGI requests.
 ; Valid syntaxes are:
 ;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
@@ -218,7 +239,7 @@ pm.max_children = 50
 ; The log file for slow requests
 ; Default Value: not set
 ; Note: slowlog is mandatory if request_slowlog_timeout is set
-;slowlog = @EXPANDED_LOCALSTATEDIR@/log/php-fpm.log.slow
+;slowlog = log/$pool.log.slow
  
 ; Set open file descriptor rlimit.
 ; Default Value: system defined value
@@ -231,13 +252,17 @@ pm.max_children = 50
  
 ; Chroot to this directory at the start. This value must be defined as an
 ; absolute path. When this value is not set, chroot is not used.
+; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
+; of its subdirectories. If the pool prefix is not set, the global prefix
+; will be used instead.
 ; Note: chrooting is a great security feature and should be used whenever 
 ;       possible. However, all PHP paths will be relative to the chroot
 ;       (error_log, sessions.save_path, ...).
 ; Default Value: not set
 ;chroot = 
  
-; Chdir to this directory at the start. This value must be an absolute path.
+; Chdir to this directory at the start.
+; Note: relative path can be used.
 ; Default Value: current directory or / when chroot
 ;chdir = /var/www
  
@@ -269,6 +294,9 @@ pm.max_children = 50
 ; overwrite previously defined php.ini values, but will append the new value
 ; instead.
 
+; Note: path INI options can be relative and will be expanded with the prefix
+; (pool, global or @prefix@)
+
 ; Default Value: nothing is defined by default except the values in php.ini and
 ;                specified at startup with the -d argument
 ;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com