Ref-count increment is not required because callers are supposed to
pass exclusive ownership while setting value of a key. What you are
seeing is result of bad-usage (where caller probably free'ed up the
json_object after setting it as value of some key).

To understand this better, look at some call-sites. Let us take an
example, say set <x> = <y>;

It calls msgSetJSONFromVar from execSet. Notice how in each scenario
the json_object passed to msgAddJSON is an exclusive copy.

If you go through all the call-sites that invoke msgAddJSON, you'll
see that exclusive-copy-only is an invariant that is always
maintained.

The "bad-usage" that I mentioned above means some caller has not
maintained that invariant, and that lead to the json_object being
free'ed after adding it to msg.

Am I making sense?

On Wed, Jun 10, 2015 at 2:39 AM, David Boles (dboles) <[email protected]> wrote:
> I finished setting up a debugging environment for 8.10.0 and spent some time 
> stepping through the code. First, I noticed that the json_object that was in 
> pMsg got overwritten on an unrelated call to json_object_new_object(). That 
> suggested that the json-c library thought it owned that memory while a pMsg 
> still had a pointer to it. In reading the code I noticed that in msgDelJSON() 
> under the condition "if(name[1] == '\0')" there is a  json_object_put() but 
> under the matching condition in msgAddJSON() there is no json_object_get().
>
> After more reading through the code to get a rough idea of code flow I 
> applied the following delta:
>
> --- ../pristine/rsyslog-8.10.0/runtime/msg.c    2015-05-19 03:53:40.000000000 
> -0500
> +++ runtime/msg.c       2015-06-09 14:47:51.066158522 -0500
> @@ -4343,6 +4343,7 @@
>         }
>
>         if(name[1] == '\0') { /* full tree? */
> +               json_object_get(json);
>                 if(*pjroot == NULL)
>                         *pjroot = json;
>                 else
>
> and rebuilt. That change eliminated the overwriting of the pMsg->json object 
> and eliminated the segfault. I then performed the following steps:
>
>   (1) launched the patched rsyslogd
>   (2) pumped 100K messages through it to reach steady state
>   (3) measured the memory footprint of rsyslogd
>   (4) pumped another 100K messages through it
>   (5) measured the RSS memory footprint of rsyslogd again and verified that 
> there was no change
>
> I don't know whether my patch is correct or not but I'll be continuing down 
> this path until I get feedback. If it is correct, then it's a fix for issue 
> #368.
>
> Thanks,
>
> David Boles
> ________________________________________
> From: David Boles (dboles)
> Sent: Monday, June 08, 2015 4:36 PM
> To: rsyslog-users
> Subject: RE: [rsyslog] Unable to use foreach
>
> Note - I don't get a segfault when mmexternal is enabled unless I also access 
> $!. I can log the contents of my mmexternal rewritten "msg" without error. 
> However, because I'm logging "msg" I get a quote-ified representation of the 
> JSON that I want.
>
> Even if my Python script doesn't modify anything but "msg", as soon as I 
> either do the RSYSLOG_DebugFormat action (which accesses $!) or perform 
> either an omfile or ommongodb action with a template referencing $! then 
> rsyslogd segfaults. I don't know rsyslogd internals at all but below you'll 
> find a backtrace from the point of the segfault. I've confirmed that it is 
> getting the "!" property and am working to set up a debugging environment now.
>
> This has been filed as an issue per Rainer's request several days ago.
>
> Thanks,
>
> David Boles
>
> ========
>
> Program received signal SIGSEGV, Segmentation fault.
> [Switching to Thread 0x7ffff4a2e700 (LWP 4761)]
> printbuf_reset (p=0xffffffffffffffff) at printbuf.c:182
> 182       p->buf[0] = '\0';
> Missing separate debuginfos, use: debuginfo-install glib2-2.40.0-4.el7.x86_64 
> glibc-2.17-55.el7.x86_64 l
> iblogging-1.0.5-1.el7.x86_64 libmongo-client-0.1.8-1.el7.x86_64 
> libuuid-2.23.2-22.el7_1.x86_64 systemd-l
> ibs-208-20.el7_1.3.x86_64 zlib-1.2.7-13.el7.x86_64
> (gdb) bt
> #0  printbuf_reset (p=0xffffffffffffffff) at printbuf.c:182
> #1  0x00007ffff719247e in json_object_to_json_string_ext (jso=0x7fffe8002580, 
> flags=flags@entry=1)
>     at json_object.c:274
> #2  0x00007ffff71924da in json_object_to_json_string (jso=<optimized out>) at 
> json_object.c:286
> #3  0x00007ffff7192d15 in json_object_get_string (jso=<optimized out>) at 
> json_object.c:738
> #4  0x000000000041b7d1 in getJSONPropVal (pMsg=<optimized out>, 
> pProp=0x6aaa00,
>     pRes=pRes@entry=0x7ffff4a2d968, buflen=buflen@entry=0x7ffff4a2d95c, 
> pbMustBeFreed=0x7ffff4a2da42)
>     at msg.c:2802
> #5  0x000000000041bbcc in MsgGetProp (pMsg=pMsg@entry=0x7ffff0004030, 
> pTpe=pTpe@entry=0x6aa9e0,
>     pProp=pProp@entry=0x6aaa00, pPropLen=pPropLen@entry=0x7ffff4a2da44,
>     pbMustBeFreed=pbMustBeFreed@entry=0x7ffff4a2da42, 
> ttNow=ttNow@entry=0x7ffff4a2db10) at msg.c:3285
> #6  0x000000000044472c in tplToString (pTpl=0x6a8700, 
> pMsg=pMsg@entry=0x7ffff0004030,
>     iparam=iparam@entry=0x7fffe8002940, ttNow=ttNow@entry=0x7ffff4a2db10) at 
> ../template.c:194
> #7  0x000000000043fbff in prepareDoActionParams (pWti=0x6b1fc0, 
> ttNow=0x7ffff4a2db10,
>     pMsg=0x7ffff0004030, pAction=0x6b9680) at ../action.c:933
> #8  processMsgMain (pAction=pAction@entry=0x6b9680, pWti=pWti@entry=0x6b1fc0, 
> pMsg=0x7ffff0004030,
>     ttNow=ttNow@entry=0x7ffff4a2db10) at ../action.c:1300
> #9  0x0000000000440337 in doSubmitToActionQ (pAction=0x6b9680, pWti=0x6b1fc0, 
> pMsg=<optimized out>)
>     at ../action.c:1449
> #10 0x00000000004390a7 in execAct (stmt=0x6b95f0, pWti=0x6b1fc0, 
> pMsg=0x7ffff0004030) at ruleset.c:202
> #11 scriptExec (root=<optimized out>, pMsg=pMsg@entry=0x7ffff0004030, 
> pWti=pWti@entry=0x6b1fc0)
>     at ruleset.c:454
> #12 0x0000000000439884 in processBatch (pBatch=0x6b1ff8, pWti=0x6b1fc0) at 
> ruleset.c:508
> #13 0x0000000000448cd4 in msgConsumer (notNeeded=<optimized out>, 
> pBatch=0x6b1ff8, pWti=0x6b1fc0)
>     at rsyslogd.c:575
> #14 0x00000000004357ee in ConsumerReg (pThis=0x6b1b20, pWti=0x6b1fc0) at 
> queue.c:1897
> #15 0x000000000043182e in wtiWorker (pThis=pThis@entry=0x6b1fc0) at wti.c:334
> #16 0x000000000043001a in wtpWorker (arg=0x6b1fc0) at wtp.c:389
> #17 0x00007ffff79b1df3 in start_thread () from /lib64/libpthread.so.0
> #18 0x00007ffff6aba3dd in clone () from /lib64/libc.so.6
> (gdb) list
> 177       }
> 178     }
> 179
> 180     void printbuf_reset(struct printbuf *p)
> 181     {
> 182       p->buf[0] = '\0';
> 183       p->bpos = 0;
> 184     }
> 185
> 186     void printbuf_free(struct printbuf *p)
> (gdb) p p
> $1 = (struct printbuf *) 0xffffffffffffffff
>
>
> ________________________________________
> From: [email protected] [[email protected]] 
> on behalf of David Lang [[email protected]]
> Sent: Monday, June 08, 2015 3:40 PM
> To: rsyslog-users
> Subject: Re: [rsyslog] Unable to use foreach
>
> given that input, what does your python script output?
>
> since you are getting a segfault when mmexternal is enabled, I would guess 
> that
> you are returning something invalid and rsyslog is not properly handling the
> error.
>
> I'll also point out that what you are passing to your script is much larger 
> than
> it needs to be (you have the same data in the original message, in rawmessge, 
> in
> $! and sometimes in other things). I would suggest creating a template that 
> just
> has $! in it, and set variables for anything you need that's not already in
> there.
>
> David Lang
>
> On Mon, 8 Jun 2015, David Boles (dboles) wrote:
>
>> Date: Mon, 8 Jun 2015 20:34:45 +0000
>> From: "David Boles (dboles)" <[email protected]>
>> Reply-To: rsyslog-users <[email protected]>
>> To: rsyslog-users <[email protected]>
>> Subject: Re: [rsyslog] Unable to use foreach
>>
>> David,
>>
>> Sorry for the delay in responding - my focus was elsewhere temporarily. What 
>> you say about $! and new content below makes sense. I've tried an experiment 
>> with the RSYSLOG_DebugFormat mechanism by running with the following 
>> /etc/rsyslog.conf tweaked in three ways:
>>
>> --------
>> module(load="imuxsock")
>> module(load="mmexternal")
>> module(load="mmjsonparse")
>> module(load="ommongodb")
>>
>> template(name="yyd-log-info" type="list") {
>>    property(name="timestamp")
>>    constant(value=" ")
>>    property(name="hostname")
>>    constant(value=" ")
>>    property(name="msg")
>>    constant(value="\n")
>> }
>>
>> # 1
>> *.* /var/log/debug_pre.log;RSYSLOG_DebugFormat
>>
>> if ($fromhost-ip == '127.0.0.1' and $syslogfacility-text != 'kern') then {
>>    action(type="mmjsonparse")
>>    if $parsesuccess == "OK" then {
>> # 2
>>        action(type="mmexternal"
>>               binary="/tmp/snowflake/rewriter.py"
>>               interface.input="fulljson" )
>>        action(type="omfile"
>>               template="yyd-log-info"
>>               file="/var/log/yyd_anon.log")
>>    }
>> }
>>
>> # 3
>> #*.* /var/log/debug_post.log;RSYSLOG_DebugFormat
>> --------
>>
>> (1) One mode was with the mmexternal action after # 2 commented out and the 
>> DebugFormat action at # 3, yielding:
>> --------
>> Debug line with all properties:
>> FROMHOST: 'dboles-victim-0', fromhost-ip: '127.0.0.1', HOSTNAME: 
>> 'dboles-victim-0', PRI: 12,
>> syslogtag ':', programname: '', APP-NAME: '', PROCID: '-', MSGID: '-',
>> TIMESTAMP: 'Jun  8 15:37:24', STRUCTURED-DATA: '-',
>> msg: ' @cee:{"msg":"[umberlog test] testing ... 1, 2, 
>> 3\n","count":"2","el.0.type":"ladybug","el.0.name":"natalie","el.0.spots":"12","el.1.type":"ant","el.1.name":"B289","el.1.role":"worker","el.1.age":"9","pid":"32294","facility":"user","priority":"warn","uid":"1002","gid":"1002","host":"dboles-victim-0","program":"","timestamp":"2015-06-08T15:37:24.449217401-0400"}'
>> escaped msg: ' @cee:{"msg":"[umberlog test] testing ... 1, 2, 
>> 3\n","count":"2","el.0.type":"ladybug","el.0.name":"natalie","el.0.spots":"12","el.1.type":"ant","el.1.name":"B289","el.1.role":"worker","el.1.age":"9","pid":"32294","facility":"user","priority":"warn","uid":"1002","gid":"1002","host":"dboles-victim-0","program":"","timestamp":"2015-06-08T15:37:24.449217401-0400"}'
>> inputname: imuxsock rawmsg: '<12>Jun  8 15:37:24 : @cee:{"msg":"[umberlog 
>> test] testing ... 1, 2, 
>> 3\n","count":"2","el.0.type":"ladybug","el.0.name":"natalie","el.0.spots":"12","el.1.type":"ant","el.1.name":"B289","el.1.role":"worker","el.1.age":"9","pid":"32294","facility":"user","priority":"warn","uid":"1002","gid":"1002","host":"dboles-victim-0","program":"","timestamp":"2015-06-08T15:37:24.449217401-0400"}'
>> $!:{ "msg": "[umberlog test] testing ... 1, 2, 3\n", "count": "2", 
>> "el.0.type": "ladybug", "el.0.name": "natalie", "el.0.spots": "12", 
>> "el.1.type": "ant", "el.1.name": "B289", "el.1.role": "worker", "el.1.age": 
>> "9", "pid": "32294", "facility": "user", "priority": "warn", "uid": "1002", 
>> "gid": "1002", "host": "dboles-victim-0", "program": "", "timestamp": 
>> "2015-06-08T15:37:24.449217401-0400" }
>> $.:
>> $/:
>> --------
>> This makes sense - the mmjsonparse populates the $! element.
>>
>> (2) The second mode was with the mmexternal action at # 2 enabled and the 
>> DebugFormat action at # 3 - this results in rsyslogd suffering a segfault.
>>
>>
>> (3) The third mode has the mmexternal action at # 2 enabled and the 
>> DebugFormat action at #1 enabled:
>>
>> Debug line with all properties:
>> FROMHOST: 'dboles-victim-0', fromhost-ip: '127.0.0.1', HOSTNAME: 
>> 'dboles-victim-0', PRI: 12,
>> syslogtag ':', programname: '', APP-NAME: '', PROCID: '-', MSGID: '-',
>> TIMESTAMP: 'Jun  8 15:41:26', STRUCTURED-DATA: '-',
>> msg: ' @cee:{"msg":"[umberlog test] testing ... 1, 2, 
>> 3\n","count":"2","el.0.type":"ladybug","el.0.name":"natalie","el.0.spots":"12","el.1.type":"ant","el.1.name":"B289","el.1.role":"worker","el.1.age":"9","pid":"1828","facility":"user","priority":"warn","uid":"1002","gid":"1002","host":"dboles-victim-0","program":"","timestamp":"2015-06-08T15:41:26.302102005-0400"}'
>> escaped msg: ' @cee:{"msg":"[umberlog test] testing ... 1, 2, 
>> 3\n","count":"2","el.0.type":"ladybug","el.0.name":"natalie","el.0.spots":"12","el.1.type":"ant","el.1.name":"B289","el.1.role":"worker","el.1.age":"9","pid":"1828","facility":"user","priority":"warn","uid":"1002","gid":"1002","host":"dboles-victim-0","program":"","timestamp":"2015-06-08T15:41:26.302102005-0400"}'
>> inputname: imuxsock rawmsg: '<12>Jun  8 15:41:26 : @cee:{"msg":"[umberlog 
>> test] testing ... 1, 2, 
>> 3\n","count":"2","el.0.type":"ladybug","el.0.name":"natalie","el.0.spots":"12","el.1.type":"ant","el.1.name":"B289","el.1.role":"worker","el.1.age":"9","pid":"1828","facility":"user","priority":"warn","uid":"1002","gid":"1002","host":"dboles-victim-0","program":"","timestamp":"2015-06-08T15:41:26.302102005-0400"}'
>> $!:
>> $.:
>> $/:
>>
>> --------
>>
>> You also asked for what my external Python script gets as input:
>>
>> { "msg": " @cee:{\"msg\":\"[umberlog test] testing ... 1, 2, 
>> 3\\n\",\"count\":\"2\",\"el.0.type\":\"
>> ladybug\",\"el.0.name\":\"natalie\",\"el.0.spots\":\"12\",\"el.1.type\":\"ant\",\"el.1.name\":\"B289
>> \",\"el.1.role\":\"worker\",\"el.1.age\":\"9\",\"pid\":\"4440\",\"facility\":\"user\",\"priority\":\
>> "warn\",\"uid\":\"1002\",\"gid\":\"1002\",\"host\":\"dboles-victim-0\",\"program\":\"\",\"timestamp\
>> ":\"2015-06-08T16:28:12.877502675-0400\"}", "rawmsg": "<12>Jun  8 16:28:12 : 
>> @cee:{\"msg\":\"[umberl
>> og test] testing ... 1, 2, 
>> 3\\n\",\"count\":\"2\",\"el.0.type\":\"ladybug\",\"el.0.name\":\"natalie\
>> ",\"el.0.spots\":\"12\",\"el.1.type\":\"ant\",\"el.1.name\":\"B289\",\"el.1.role\":\"worker\",\"el.1
>> .age\":\"9\",\"pid\":\"4440\",\"facility\":\"user\",\"priority\":\"warn\",\"uid\":\"1002\",\"gid\":\
>> "1002\",\"host\":\"dboles-victim-0\",\"program\":\"\",\"timestamp\":\"2015-06-08T16:28:12.877502675-
>> 0400\"}", "timereported": "2015-06-08T16:28:12.877616-04:00", "hostname": 
>> "dboles-victim-0", "syslog
>> tag": ":", "inputname": "imuxsock", "fromhost": "dboles-victim-0", 
>> "fromhost-ip": "127.0.0.1", "pri"
>> : "12", "syslogfacility": "1", "syslogseverity": "4", "timegenerated": 
>> "2015-06-08T16:28:12.877616-0
>> 4:00", "programname": "", "protocol-version": "0", "structured-data": "-", 
>> "app-name": "", "procid":
>> "-", "msgid": "-", "uuid": null, "$!": { "msg": "[umberlog test] testing ... 
>> 1, 2, 3\n", "count": "
>> 2", "el.0.type": "ladybug", "el.0.name": "natalie", "el.0.spots": "12", 
>> "el.1.type": "ant", "el.1.na
>> me": "B289", "el.1.role": "worker", "el.1.age": "9", "pid": "4440", 
>> "facility": "user", "priority":
>> "warn", "uid": "1002", "gid": "1002", "host": "dboles-victim-0", "program": 
>> "", "timestamp": "2015-0
>> 6-08T16:28:12.877502675-0400" } }
>>
>>
>>
>>
>>
>> ________________________________________
>> From: [email protected] [[email protected]] 
>> on behalf of David Lang [[email protected]]
>> Sent: Monday, June 01, 2015 5:06 PM
>> To: rsyslog-users
>> Subject: Re: [rsyslog] Unable to use foreach
>>
>> On Sun, 24 May 2015, David Boles (dboles) wrote:
>>
>>> Thanks Rainer / David,
>>>
>>> Based on Rainer's input I've pivoted to going down the mmexternal path and 
>>> have questions based on experimenting with that.
>>>
>>> To answer David's "what are you trying to do" question and make things as 
>>> simple and concrete as possible I've created a clean example (log entry 
>>> generator, rsyslog configuration, mmexternal plugin, etc.) that captures 
>>> what I'm trying to do. To avoid pasting a bunch of stuff into these emails 
>>> I set up a git repo with the example at:
>>>
>>>    https://github.com/davidboles/snowflake.git
>>>
>>> The README.md explains the elements of that repo. I have the following 
>>> questions:
>>>
>>> (1) The generated result does not have the { "hostname" : "frodo" } anywhere
>>> in it. The docs say that I can add elements to the JSON elements of the
>>> message, although that sentence is ambiguous as to whether I can do so if I
>>> haven't modified some other element.
>>
>> sorry for the delay in responding
>>
>> in rsyslog, you don't have many elements. There are only a handful of 
>> built-in
>> properties and then everything else is in $!
>>
>> so if you want to modify anything in $!, you must output a completely new $!
>> string (which is the JSON representation of the tree.
>>
>> If you are wanting to add a new item "elements", that means that you are 
>> going
>> to modify the $! variable to include your new element.
>>
>> the section talking about "if you modify the message variable tree" is saying
>> that if you don't change $!, you can't add new variables, because all 
>> variables
>> are inside of $!.
>>
>> although, since this talks about the inability to delete things, it may be 
>> that
>> what you output is merged with what's already in $!. I'd have to test this 
>> (see
>> how to test below)
>>
>>> (2) If I uncomment the template line (line 20 of build.conf) then syslog
>>> SEGV's. How do I access the jsonmesg content after getting stuff back 
>>> through
>>> mmexternal?
>>
>> it's not line 20 in the currently visible file, but the best way to 
>> troubleshoot
>> "how do I access blah" is to write a file with the format RSYSLOG_DebugFormat
>> and it will show you all the variables that are defined at that point. In a
>> situation like yours where you are doing multiple parse actions, it will show
>> you the variables as of that point in the processing.
>>
>> so, to see how to access the variables that were set with mmexternal, after 
>> that
>> action add another one
>> /var/log/test-debug;RSYSLOG_DebugFormat
>>
>> and then look at the $!: line in that debug output. It will show you exactly
>> what variables are defined at that point.
>>
>> I suspect that what you are outputting doesn't quite match
>>
>>> (3) The field "MUTABLE" has the value "Alpha" in the input to the mmexternal
>>> unit (I can see it in /tmp/rewriter.trace entries). What JSON should my
>>> rewriter.py emit to cause that to be rewritten to "Beta"?
>>
>> I think it would be useful for you to show what the string is that 
>> rewriter.py
>> receives, what it outputs, and what the resulting debug is. I think that will
>> make it pretty obvious as to what's happening
>>
>> David Lang
>>
>>> Thanks,
>>>
>>> David Boles
>>>
>>>
>>>
>>> ________________________________________
>>> From: [email protected] [[email protected]] 
>>> on behalf of Rainer Gerhards [[email protected]]
>>> Sent: Friday, May 22, 2015 5:05 PM
>>> To: rsyslog-users
>>> Subject: Re: [rsyslog] Unable to use foreach
>>>
>>> External plugin interface:
>>> https://github.com/rsyslog/rsyslog/blob/master/plugins/external/INTERFACE.md
>>>
>>> Overview :
>>> https://github.com/rsyslog/rsyslog/blob/master/plugins/external/README.md
>>>
>>> Sent from phone, thus brief.
>>> _______________________________________________
>>> rsyslog mailing list
>>> http://lists.adiscon.net/mailman/listinfo/rsyslog
>>> http://www.rsyslog.com/professional-services/
>>> What's up with rsyslog? Follow https://twitter.com/rgerhards
>>> NOTE WELL: This is a PUBLIC mailing list, posts are ARCHIVED by a myriad of 
>>> sites beyond our control. PLEASE UNSUBSCRIBE and DO NOT POST if you DON'T 
>>> LIKE THAT.
>>>
>> _______________________________________________
>> rsyslog mailing list
>> http://lists.adiscon.net/mailman/listinfo/rsyslog
>> http://www.rsyslog.com/professional-services/
>> What's up with rsyslog? Follow https://twitter.com/rgerhards
>> NOTE WELL: This is a PUBLIC mailing list, posts are ARCHIVED by a myriad of 
>> sites beyond our control. PLEASE UNSUBSCRIBE and DO NOT POST if you DON'T 
>> LIKE THAT.
>> _______________________________________________
>> rsyslog mailing list
>> http://lists.adiscon.net/mailman/listinfo/rsyslog
>> http://www.rsyslog.com/professional-services/
>> What's up with rsyslog? Follow https://twitter.com/rgerhards
>> NOTE WELL: This is a PUBLIC mailing list, posts are ARCHIVED by a myriad of 
>> sites beyond our control. PLEASE UNSUBSCRIBE and DO NOT POST if you DON'T 
>> LIKE THAT.
>>
> _______________________________________________
> rsyslog mailing list
> http://lists.adiscon.net/mailman/listinfo/rsyslog
> http://www.rsyslog.com/professional-services/
> What's up with rsyslog? Follow https://twitter.com/rgerhards
> NOTE WELL: This is a PUBLIC mailing list, posts are ARCHIVED by a myriad of 
> sites beyond our control. PLEASE UNSUBSCRIBE and DO NOT POST if you DON'T 
> LIKE THAT.
> _______________________________________________
> rsyslog mailing list
> http://lists.adiscon.net/mailman/listinfo/rsyslog
> http://www.rsyslog.com/professional-services/
> What's up with rsyslog? Follow https://twitter.com/rgerhards
> NOTE WELL: This is a PUBLIC mailing list, posts are ARCHIVED by a myriad of 
> sites beyond our control. PLEASE UNSUBSCRIBE and DO NOT POST if you DON'T 
> LIKE THAT.



-- 
Regards,
Janmejay
http://codehunk.wordpress.com
_______________________________________________
rsyslog mailing list
http://lists.adiscon.net/mailman/listinfo/rsyslog
http://www.rsyslog.com/professional-services/
What's up with rsyslog? Follow https://twitter.com/rgerhards
NOTE WELL: This is a PUBLIC mailing list, posts are ARCHIVED by a myriad of 
sites beyond our control. PLEASE UNSUBSCRIBE and DO NOT POST if you DON'T LIKE 
THAT.

Reply via email to