Yearly Archives: 2017


Send ALT+CTRL+Delete to QEMU virtual machine 1

Recently we wanted to start a Windows virtual machine from a physical hard disk using a Fedora w/ GNOME 3 host machine to change the domain password of a user.
To do so, we used QEMU, QEMU is a generic and open source machine emulator and virtualizer.

To perform the password change, we needed to sent the ALT+CTRL+Delete key combination to the virtual machine to access the system screen and then change the user password.
Pressing ALT+CTRL+Delete on the Fedora/GNOME 3 host machine, it popped up a prompt to shut down the host machine instead of sending the key combination to the active window of the VM. Apparently, we could not sent the key combination directly to the VM and had to find a way around it.

Solution:

We pressed ALT+CTRL+2 while the QEMU window was selected/active to switch to the QEMU terminal/monitor.
In the blank screen that appeared, we typed sendkey alt-ctrl-delete and pressed the Enter key.
This action sent to the virtual machine OS the key combination ALT+CTRL+Delete.
Finally, to switch back  to the guest screen we pressed ALT+CTRL+1.


Reading data from /dev/i2c-%d

The following code will read a byte from position 0x10, of the register at 0x3f of the device /dev/i2c-2.

To compile this code, you need the helper library i2c-dev.h which can be found in the download package here: [download id=”3312″]

main.c

//Based on https://www.kernel.org/doc/Documentation/i2c/dev-interface

#include "linux/i2c-dev.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>

int main() {
    const int adapter_nr = 2;
    char filename[20];
    snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
    const int file = open(filename, O_RDWR);
    if (file < 0) {
        printf("Oh dear, something went wrong with open()! %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    // The I2C address. Got it from `i2cdetect -y 2;`
    const int addr = 0x3f;

    if (ioctl(file, I2C_SLAVE, addr) < 0) {
        printf("Oh dear, something went wrong with ioctl()! %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    // Device register to access.
    const __u8 reg = 0x10;
    // Using SMBus commands
    const __s32 result = i2c_smbus_read_byte_data(file, reg);
    if (result < 0) {
         // ERROR HANDLING: i2c transaction failed
         printf("Oh dear, something went wrong with i2c_smbus_read_byte_data()>i2c_smbus_access()>ioctl()! %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    } else {
        // res contains the read word
        printf("0x%+02x\n", result);
    }

    close(file);

    return(EXIT_SUCCESS);
}

linux/i2c-dev.h

/*
    i2c-dev.h - i2c-bus driver, char device interface

    Copyright (C) 1995-97 Simon G. Vogl
    Copyright (C) 1998-99 Frodo Looijaard <[email protected]>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
    MA 02110-1301 USA.
*/

#ifndef _LINUX_I2C_DEV_H
#define _LINUX_I2C_DEV_H

#include <linux/types.h>
#include <sys/ioctl.h>
#include <stddef.h>


/* -- i2c.h -- */


/*
 * I2C Message - used for pure i2c transaction, also from /dev interface
 */
struct i2c_msg {
    __u16 addr;    /* slave address            */
    unsigned short flags;
#define I2C_M_TEN    0x10    /* we have a ten bit chip address    */
#define I2C_M_RD    0x01
#define I2C_M_NOSTART    0x4000
#define I2C_M_REV_DIR_ADDR    0x2000
#define I2C_M_IGNORE_NAK    0x1000
#define I2C_M_NO_RD_ACK        0x0800
    short len;        /* msg length                */
    char *buf;        /* pointer to msg data            */
};

/* To determine what functionality is present */

#define I2C_FUNC_I2C            0x00000001
#define I2C_FUNC_10BIT_ADDR        0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING    0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
#define I2C_FUNC_SMBUS_PEC        0x00000008
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL    0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK        0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE    0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE    0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA    0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA    0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA    0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA    0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL    0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA    0x01000000
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK    0x04000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK    0x08000000 /* w/ 1-byte reg. addr. */

#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
                             I2C_FUNC_SMBUS_WRITE_BYTE)
#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
                                  I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
                                  I2C_FUNC_SMBUS_WRITE_WORD_DATA)
#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
                                   I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
                                  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)

/* Old name, for compatibility */
#define I2C_FUNC_SMBUS_HWPEC_CALC    I2C_FUNC_SMBUS_PEC

/*
 * Data for SMBus Messages
 */
#define I2C_SMBUS_BLOCK_MAX    32    /* As specified in SMBus standard */
#define I2C_SMBUS_I2C_BLOCK_MAX    32    /* Not specified but we use same structure */
union i2c_smbus_data {
    __u8 byte;
    __u16 word;
    __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
                                                /* and one more for PEC */
};

/* smbus_access read or write markers */
#define I2C_SMBUS_READ    1
#define I2C_SMBUS_WRITE    0

/* SMBus transaction types (size parameter in the above functions)
   Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */
#define I2C_SMBUS_QUICK            0
#define I2C_SMBUS_BYTE            1
#define I2C_SMBUS_BYTE_DATA        2
#define I2C_SMBUS_WORD_DATA        3
#define I2C_SMBUS_PROC_CALL        4
#define I2C_SMBUS_BLOCK_DATA        5
#define I2C_SMBUS_I2C_BLOCK_BROKEN  6
#define I2C_SMBUS_BLOCK_PROC_CALL   7        /* SMBus 2.0 */
#define I2C_SMBUS_I2C_BLOCK_DATA    8


/* /dev/i2c-X ioctl commands.  The ioctl's parameter is always an
 * unsigned long, except for:
 *    - I2C_FUNCS, takes pointer to an unsigned long
 *    - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data
 *    - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data
 */
#define I2C_RETRIES    0x0701    /* number of times a device address should
                   be polled when not acknowledging */
#define I2C_TIMEOUT    0x0702    /* set timeout in units of 10 ms */

/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
 * are NOT supported! (due to code brokenness)
 */
#define I2C_SLAVE    0x0703    /* Use this slave address */
#define I2C_SLAVE_FORCE    0x0706    /* Use this slave address, even if it
                   is already in use by a driver! */
#define I2C_TENBIT    0x0704    /* 0 for 7 bit addrs, != 0 for 10 bit */

#define I2C_FUNCS    0x0705    /* Get the adapter functionality mask */

#define I2C_RDWR    0x0707    /* Combined R/W transfer (one STOP only) */

#define I2C_PEC        0x0708    /* != 0 to use PEC with SMBus */
#define I2C_SMBUS    0x0720    /* SMBus transfer */


/* This is the structure as used in the I2C_SMBUS ioctl call */
struct i2c_smbus_ioctl_data {
    __u8 read_write;
    __u8 command;
    __u32 size;
    union i2c_smbus_data *data;
};

/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
    struct i2c_msg *msgs;    /* pointers to i2c_msgs */
    __u32 nmsgs;            /* number of i2c_msgs */
};

#define  I2C_RDRW_IOCTL_MAX_MSGS    42


static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
                                     int size, union i2c_smbus_data *data)
{
    struct i2c_smbus_ioctl_data args;

    args.read_write = read_write;
    args.command = command;
    args.size = size;
    args.data = data;
    return ioctl(file,I2C_SMBUS,&args);
}


static inline __s32 i2c_smbus_write_quick(int file, __u8 value)
{
    return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL);
}

static inline __s32 i2c_smbus_read_byte(int file)
{
    union i2c_smbus_data data;
    if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
        return -1;
    else
        return 0x0FF & data.byte;
}

static inline __s32 i2c_smbus_write_byte(int file, __u8 value)
{
    return i2c_smbus_access(file,I2C_SMBUS_WRITE,value,
                            I2C_SMBUS_BYTE,NULL);
}

static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
{
    union i2c_smbus_data data;
    if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                         I2C_SMBUS_BYTE_DATA,&data))
        return -1;
    else
        return 0x0FF & data.byte;
}

static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command,
                                              __u8 value)
{
    union i2c_smbus_data data;
    data.byte = value;
    return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                            I2C_SMBUS_BYTE_DATA, &data);
}

static inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
{
    union i2c_smbus_data data;
    if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                         I2C_SMBUS_WORD_DATA,&data))
        return -1;
    else
        return 0x0FFFF & data.word;
}

static inline __s32 i2c_smbus_write_word_data(int file, __u8 command,
                                              __u16 value)
{
    union i2c_smbus_data data;
    data.word = value;
    return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                            I2C_SMBUS_WORD_DATA, &data);
}

static inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
{
    union i2c_smbus_data data;
    data.word = value;
    if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                         I2C_SMBUS_PROC_CALL,&data))
        return -1;
    else
        return 0x0FFFF & data.word;
}


/* Returns the number of read bytes */
static inline __s32 i2c_smbus_read_block_data(int file, __u8 command,
                                              __u8 *values)
{
    union i2c_smbus_data data;
    int i;
    if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                         I2C_SMBUS_BLOCK_DATA,&data))
        return -1;
    else {
        for (i = 1; i <= data.block[0]; i++)
            values[i-1] = data.block[i];
        return data.block[0];
    }
}

static inline __s32 i2c_smbus_write_block_data(int file, __u8 command,
                                               __u8 length, const __u8 *values)
{
    union i2c_smbus_data data;
    int i;
    if (length > 32)
        length = 32;
    for (i = 1; i <= length; i++)
        data.block[i] = values[i-1];
    data.block[0] = length;
    return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                            I2C_SMBUS_BLOCK_DATA, &data);
}

/* Returns the number of read bytes */
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
   ask for less than 32 bytes, your code will only work with kernels
   2.6.23 and later. */
static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command,
                                                  __u8 length, __u8 *values)
{
    union i2c_smbus_data data;
    int i;

    if (length > 32)
        length = 32;
    data.block[0] = length;
    if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
                         length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
                          I2C_SMBUS_I2C_BLOCK_DATA,&data))
        return -1;
    else {
        for (i = 1; i <= data.block[0]; i++)
            values[i-1] = data.block[i];
        return data.block[0];
    }
}

static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command,
                                                   __u8 length,
                                                   const __u8 *values)
{
    union i2c_smbus_data data;
    int i;
    if (length > 32)
        length = 32;
    for (i = 1; i <= length; i++)
        data.block[i] = values[i-1];
    data.block[0] = length;
    return i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                            I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
}

/* Returns the number of read bytes */
static inline __s32 i2c_smbus_block_process_call(int file, __u8 command,
                                                 __u8 length, __u8 *values)
{
    union i2c_smbus_data data;
    int i;
    if (length > 32)
        length = 32;
    for (i = 1; i <= length; i++)
        data.block[i] = values[i-1];
    data.block[0] = length;
    if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command,
                         I2C_SMBUS_BLOCK_PROC_CALL,&data))
        return -1;
    else {
        for (i = 1; i <= data.block[0]; i++)
            values[i-1] = data.block[i];
        return data.block[0];
    }
}


#endif /* _LINUX_I2C_DEV_H */

Online Qualification Round Problem for Google Hash Code 2017

[download id=”2740″]

[download id=”2739″]

Streaming videos

Problem statement for Online Qualification Round, Hash Code 2017

Introduction

Have you ever wondered what happens behind the scenes when you watch a YouTube video? As more and more people watch online videos (and as the size of these videos increases), it is critical that video-serving infrastructure is optimized to handle requests reliably and quickly.

This typically involves putting in place cache servers, which store copies of popular videos. When a user request for a particular video arrives, it can be handled by a cache server close to the user, rather than by a remote data center thousands of kilometers away.

But how should you decide which videos to put in which cache servers?

Task

Given a description of cache servers, network endpoints and videos, along with predicted requests for individual videos, decide which videos to put in which cache server in order to minimize the average waiting time for all requests.

Problem description

The picture below represents the video serving network.

Videos

Each video has a size given in megabytes (MB). The data center stores ​ all videos​ . Additionally, each video can be put in 0, 1, or more cache servers​. Each cache server has a maximum capacity given in megabytes.

Endpoints

Each endpoint represents a group of users connecting to the Internet in the same geographical area (for example, a neighborhood in a city). Every endpoint is connected to the data center. Additionally, each endpoint may (but doesn’t have to) be connected to 1 or more cache servers​ .

Each endpoint is characterized by the latency of its connection to the data center (how long it takes to serve a video from the data center to a user in this endpoint), and by the latencies to each cache server that the endpoint is connected to (how long it takes to serve a video stored in the given cache server to a user in this endpoint).

Requests

The predicted requests provide data on how many times a particular video is requested from a particular endpoint.

Input data set

The input data is provided as a data set file – a plain text file containing exclusively ASCII characters with a single \n character at the end of each line (UNIX-​ style line endings).

Videos, endpoints and cache servers are referenced by integer IDs. There are V videos numbered from 0 to V − 1 , E endpoints numbered from 0 to E − 1 and C cache servers numbered from 0 to C − 1 .

File format

All numbers mentioned in the specification are natural numbers that fit within the indicated ranges. When multiple numbers appear in a single line, they are separated by a single space.

The first line of the input contains the following numbers:

  • V ( 1 ≤ V ≤ 10000) – the number of videos
  • E ( 1 ≤ E ≤ 1000) – the number of endpoints
  • R ( 1 ≤ R ≤ 1000000) – the number of request descriptions
  • C ( 1 ≤ C ≤ 1000) – the number of cache servers
  • X ( 1 ≤ X ≤ 500000) – the capacity of each cache server in megabytes

The next line contains ​V numbers describing the sizes of individual videos in megabytes: S0, S1, … SV-1. Si is the size of video i​ in megabytes ( 1 ≤ Si ≤ 1000).

The next section describes each of the endpoints one after another, from endpoint 0 to endpoint E − 1 . The description of each endpoint consists of the following lines:

  • a line containing two numbers:
    • LD ( 2 ≤ LD ≤ 4000) – the latency of serving a video request from the data center to this endpoint, in milliseconds
    • K ( 0 ≤ K ≤ C ) – the number of cache servers that this endpoint is connected to
  • K lines describing the connections from the endpoint to each of the K connected cache servers.
    Each line contains the following numbers:

    • c ( 0 ≤ c < C ) – the ID of the cache server
    • Lc ( 1 ≤ Lc ≤ 500) – the latency of serving a video request from this cache server to this endpoint, in milliseconds. You can assume that latency from the cache is strictly lower than latency from the data center ( 1 ≤ Lc < LD

Finally, the last section contains R request descriptions in separate lines. Each line contains the following numbers:

  • Rv ( 0 ≤ Rv < V ) – the ID of the requested video
  • Re ( 0 ≤ Re < E ) – the ID of the endpoint from which the requests are coming from
  • Rn ( 0 < Rn ≤ 10000) – the number of requests

Example

5 2 4 3 100
50 50 80 30 110
1000 3
0 100
2 200
1 300
500 0
3 0 1500
0 1 1000
4 0 500
1 0 1000

Example input file explanation.

5 videos, 2 endpoints, 4 request descriptions, 3 caches 100MB each.
Videos 0, 1, 2, 3, 4 have sizes 50MB, 50MB, 80MB, 30MB, 110MB.
Endpoint 0 has 1000ms datacenter latency and is connected to 3 caches:
The latency (of endpoint 0) to cache 0 is 100ms.
The latency (of endpoint 0) to cache 2 is 200ms.
The latency (of endpoint 0) to cache 1 is 200ms.
Endpoint 1 has 500ms datacenter latency and is not connected to a cache.
1500 requests for video 3 coming from endpoint 0.
1000 requests for video 0 coming from endpoint 1.
500 requests for video 4 coming from endpoint 0.
1000 requests for video 1 coming from endpoint 0.

Connections and latencies between the endpoints and caches of example input.

Submissions

File format

Your submission should start with a line containing a single number N ( 0 ≤ N ≤ C ) – the number of cache server descriptions to follow.

Each of the subsequent N lines should describe the videos cached in a single cache server. It should contain the following numbers:

  • c ( 0 ≤ c < C ) – the ID of the cache server being described,
  • the IDs of the videos stored in this cache server: v0, …, vn ( 0 ≤ vi < V) (at least 0 and at most V numbers), given in any order without repetitions

Each cache server should be described in at most one line. It is not necessary to describe all cache servers: if a cache does not occur in the submission, this cache server will be considered as empty. Cache servers can be described in any order.

Example

3
0 2
1 3 1
2 0 1

Example submission file explanation.

We are using  all 3 cache servers.
Cache server 0 contains only video 2.
Cache server 1 contains videos 3 and 1.
Cache server 2 contains videos 0 and 1.

Validation

The output file is valid if it meets the following criteria:

  • the format matches the description above
  • the total size of videos stored in each cache server does not exceed the maximum cache server capacity

Scoring

The score is the average time saved per request, in microseconds. (Note that the latencies in the input file are given in milliseconds. The score is given in microseconds to provide a better resolution of results.)
For each request description ( Rv, Re, Rn) in the input file, we choose the best way to stream the video Rv to the endpoint Re. We pick the lowest possible latency L = min(LD, L0, … , Lk−1) , where L​D is the latency of serving a video to the endpoint Re from the data center, and L0, … , Lk−1 are latencies of serving a video to the endpoint Re from each cache server that:

  • is connected to the endpoint Re, and
  • contains the video Rv

The time that was saved for each request is LD

As each request description describes Rn requests, the time saved for the entire request description is Rn × ( LD − L ) .

To compute the total score for the data set, we sum the time saved for individual request descriptions in milliseconds, multiply by 1000 and divide it by the total number of requests in all request descriptions, rounding down.

A schematic representation of the example submission file above​ .

In the example​ above, there are three request descriptions for the endpoint 0

  • 1500 requests for video 3, streamed from cache 1 with 300ms of latency, saving 1000ms − 300ms = 700ms per request
  • 500 requests for video 4, streamed from the data center, saving 0ms per request
  • 1000 requests for video 1, streamed from cache 2 with 200ms of latency saving 800ms per request

There is also one request description for the endpoint 1:

  • 1000 requests for video 0, streamed from the data center, saving 0ms per request

The average time saved is:

( 1500x700 + 500x0 + 1000x800 + 1000x0 )/(1500 + 500 + 1000 + 1000)

which equals 462.5ms. Multiplied by 1000, this gives the score of 462 500​.

Note that there are multiple data sets representing separate instances of the problem. The final score for your team will be the sum of your best scores on the individual data sets.

[download id=”2740″]

[download id=”2739″]


Jenkins: Improve the format of the email 4

Like many people, we use Jenkins to perform several tasks automatically.
Jenkins is an open source automation server, it provides hundreds of plugins to support building, deploying and automating any project.

One of the features of Jenkins is the option to send an email to the user under certain circumstances, e.g. after a build was complete.
The default layout of those emails is not so pretty, so we did some changes to it.
Note: below we present how to make the changes apply to the whole system, if you do not want to do that, you could apply these changes to individual projects only.

Using an administrative account, click on Manage Jenkins option on the left menu and in the new screen click on Configure System on the right column.
These actions will take you to the configuration page of your Jenkins installation (e.g. http://jenkins:8080/configure).

Scroll down to the category Extended E-mail Notification.

  1. From the drop down menu, change the value of Default Content Type to HTML (text/html).
  2. Update the value of Default Subject to [$BUILD_STATUS] - $PROJECT_NAME - Build # $BUILD_NUMBER ($BUILD_ID). We prefer to have the build status first (like a tag).
  3. In the box named Default Content enter the following block of data:
    $PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS.<br/>
    <br/>
    Check console <a href="$BUILD_URL">output</a> to view full results.<br/>
    If you cannot connect to the build server, check the attached logs.<br/>
    <br/>
    --<br/>
    Following is the last 100 lines of the log.<br/>
    <br/>
    --LOG-BEGIN--<br/>
    <pre style='line-height: 22px; display: block; color: #333; font-family: Monaco,Menlo,Consolas,"Courier New",monospace; padding: 10.5px; margin: 0 0 11px; font-size: 13px; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; background-color: #f5f5f5; border: 1px solid #ccc; border: 1px solid rgba(0,0,0,.15); -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px;'>
    ${BUILD_LOG, maxLines=100, escapeHtml=true}
    </pre>
    --LOG-END--
    

The email you will receive after a successful execution will be similar to the one below:

Subject: [Successful] - banana - Build # 77 (77)
 Body:

Press the Apply button to save the changes.

You will notice that in the email we mention the following: If you cannot connect to the build server, check the attached logs..
To enable the option to attach the logs, you need to configure your project itself.
Select your project from the main screen and then click on Configure on the left column.

Scroll down to Post-build Actions section.

From the Add post-built action drop down list select Editable Email Notification.

A new block will appear in the page.
Set the value of Attach Build Log drop down to Compress and Attach Build Log and then hit the Apply button.